fix: Include stderr and troubleshooting help in Claude Code errors
When Claude fails to execute, error messages now include: - Captured stderr output from the failed command - Troubleshooting commands to exec into pod and run `claude login` Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com>
This commit is contained in:
parent
4354f96351
commit
e9984ebc07
15
changelog/v0.10.12.md
Normal file
15
changelog/v0.10.12.md
Normal file
@ -0,0 +1,15 @@
|
|||||||
|
# v0.10.12
|
||||||
|
|
||||||
|
**Released:** 2026-01-29
|
||||||
|
|
||||||
|
## Changes
|
||||||
|
|
||||||
|
fix: Include stderr and troubleshooting help in Claude Code error messages
|
||||||
|
|
||||||
|
When Claude fails to execute, the error now includes:
|
||||||
|
- The actual stderr output from the failed command
|
||||||
|
- Troubleshooting commands to exec into the pod and run `claude login`
|
||||||
|
|
||||||
|
---
|
||||||
|
|
||||||
|
**Image:** `ghcr.io/orchard9/rdev-api:v0.10.12`
|
||||||
@ -24,7 +24,7 @@ spec:
|
|||||||
serviceAccountName: rdev-api
|
serviceAccountName: rdev-api
|
||||||
containers:
|
containers:
|
||||||
- name: rdev-api
|
- name: rdev-api
|
||||||
image: ghcr.io/orchard9/rdev-api:v0.10.11
|
image: ghcr.io/orchard9/rdev-api:v0.10.12
|
||||||
imagePullPolicy: Always
|
imagePullPolicy: Always
|
||||||
|
|
||||||
ports:
|
ports:
|
||||||
|
|||||||
@ -131,6 +131,7 @@ func (a *Adapter) Execute(ctx context.Context, req *domain.AgentRequest, handler
|
|||||||
// Stream and parse output
|
// Stream and parse output
|
||||||
var wg sync.WaitGroup
|
var wg sync.WaitGroup
|
||||||
var finalOutput strings.Builder
|
var finalOutput strings.Builder
|
||||||
|
var stderrOutput strings.Builder
|
||||||
var parseErr error
|
var parseErr error
|
||||||
var resultMsg *StreamMessage
|
var resultMsg *StreamMessage
|
||||||
|
|
||||||
@ -142,10 +143,10 @@ func (a *Adapter) Execute(ctx context.Context, req *domain.AgentRequest, handler
|
|||||||
resultMsg, parseErr = a.parseStreamOutput(stdout, handler, &finalOutput)
|
resultMsg, parseErr = a.parseStreamOutput(stdout, handler, &finalOutput)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
// Stream stderr as error events
|
// Stream stderr as error events and capture for error message
|
||||||
go func() {
|
go func() {
|
||||||
defer wg.Done()
|
defer wg.Done()
|
||||||
a.streamStderr(stderr, handler)
|
a.streamStderrCapture(stderr, handler, &stderrOutput)
|
||||||
}()
|
}()
|
||||||
|
|
||||||
wg.Wait()
|
wg.Wait()
|
||||||
@ -168,13 +169,17 @@ func (a *Adapter) Execute(ctx context.Context, req *domain.AgentRequest, handler
|
|||||||
result.ExitCode = 1
|
result.ExitCode = 1
|
||||||
result.Error = cmdErr
|
result.Error = cmdErr
|
||||||
}
|
}
|
||||||
|
// Include stderr and troubleshooting help in error
|
||||||
|
result.Error = a.buildErrorWithHelp(result.Error, stderrOutput.String(), namespace, podName)
|
||||||
} else if parseErr != nil {
|
} else if parseErr != nil {
|
||||||
result.ExitCode = 1
|
result.ExitCode = 1
|
||||||
result.Error = parseErr
|
result.Error = a.buildErrorWithHelp(parseErr, stderrOutput.String(), namespace, podName)
|
||||||
} else if resultMsg != nil && !resultMsg.IsSuccess() {
|
} else if resultMsg != nil && !resultMsg.IsSuccess() {
|
||||||
result.ExitCode = 1
|
result.ExitCode = 1
|
||||||
if resultMsg.Error != "" {
|
if resultMsg.Error != "" {
|
||||||
result.Error = fmt.Errorf("%s", resultMsg.Error)
|
result.Error = a.buildErrorWithHelp(fmt.Errorf("%s", resultMsg.Error), stderrOutput.String(), namespace, podName)
|
||||||
|
} else {
|
||||||
|
result.Error = a.buildErrorWithHelp(nil, stderrOutput.String(), namespace, podName)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -274,8 +279,8 @@ func (a *Adapter) parseStreamOutput(r io.Reader, handler domain.AgentEventHandle
|
|||||||
return resultMsg, nil
|
return resultMsg, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// streamStderr reads stderr and emits error events.
|
// streamStderrCapture reads stderr, emits error events, and captures output.
|
||||||
func (a *Adapter) streamStderr(r io.Reader, handler domain.AgentEventHandler) {
|
func (a *Adapter) streamStderrCapture(r io.Reader, handler domain.AgentEventHandler, capture *strings.Builder) {
|
||||||
scanner := bufio.NewScanner(r)
|
scanner := bufio.NewScanner(r)
|
||||||
buf := make([]byte, 0, 64*1024)
|
buf := make([]byte, 0, 64*1024)
|
||||||
scanner.Buffer(buf, 1024*1024)
|
scanner.Buffer(buf, 1024*1024)
|
||||||
@ -292,9 +297,43 @@ func (a *Adapter) streamStderr(r io.Reader, handler domain.AgentEventHandler) {
|
|||||||
Content: line,
|
Content: line,
|
||||||
Stream: "stderr",
|
Stream: "stderr",
|
||||||
})
|
})
|
||||||
|
|
||||||
|
// Capture stderr for error message (limit to 4KB)
|
||||||
|
if capture.Len() < 4096 {
|
||||||
|
if capture.Len() > 0 {
|
||||||
|
capture.WriteString("\n")
|
||||||
|
}
|
||||||
|
capture.WriteString(line)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// buildErrorWithHelp creates an error message with stderr output and troubleshooting help.
|
||||||
|
func (a *Adapter) buildErrorWithHelp(err error, stderr, namespace, podName string) error {
|
||||||
|
var msg strings.Builder
|
||||||
|
|
||||||
|
if err != nil {
|
||||||
|
msg.WriteString(err.Error())
|
||||||
|
} else {
|
||||||
|
msg.WriteString("claude command failed")
|
||||||
|
}
|
||||||
|
|
||||||
|
// Include stderr if available
|
||||||
|
if stderr != "" {
|
||||||
|
msg.WriteString("\n\nstderr:\n")
|
||||||
|
msg.WriteString(stderr)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Add troubleshooting help
|
||||||
|
msg.WriteString("\n\n---\nTroubleshooting:\n")
|
||||||
|
msg.WriteString("If Claude is not authenticated, run:\n")
|
||||||
|
fmt.Fprintf(&msg, " kubectl exec -it -n %s %s -- claude login\n", namespace, podName)
|
||||||
|
msg.WriteString("\nTo test Claude manually:\n")
|
||||||
|
fmt.Fprintf(&msg, " kubectl exec -it -n %s %s -- claude -p \"hello\"\n", namespace, podName)
|
||||||
|
|
||||||
|
return fmt.Errorf("%s", msg.String())
|
||||||
|
}
|
||||||
|
|
||||||
// Cancel attempts to cancel a running session.
|
// Cancel attempts to cancel a running session.
|
||||||
func (a *Adapter) Cancel(ctx context.Context, sessionID string) error {
|
func (a *Adapter) Cancel(ctx context.Context, sessionID string) error {
|
||||||
a.sessionsMu.Lock()
|
a.sessionsMu.Lock()
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user