{"record":{"id":"ea6dd6429dfe7928","repo":"charmbracelet/crush","slug":"agent-processing-failed-w","errorCode":null,"errorMessage":"agent processing failed: %w","messagePattern":"agent processing failed: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/app/app.go","lineNumber":402,"sourceCode":"\t\t_, _ = fmt.Fprintln(output)\n\t}()\n\n\tfor {\n\t\tif progress && stderrTTY {\n\t\t\t// HACK: Reinitialize the terminal progress bar on every iteration\n\t\t\t// so it doesn't get hidden by the terminal due to inactivity.\n\t\t\t_, _ = fmt.Fprintf(os.Stderr, ansi.SetIndeterminateProgressBar)\n\t\t}\n\n\t\tselect {\n\t\tcase result := <-done:\n\t\t\tstopSpinner()\n\t\t\tif result.err != nil {\n\t\t\t\tif errors.Is(result.err, context.Canceled) || errors.Is(result.err, agent.ErrRequestCancelled) {\n\t\t\t\t\tslog.Debug(\"Non-interactive: agent processing cancelled\", \"session_id\", sess.ID)\n\t\t\t\t\treturn nil\n\t\t\t\t}\n\t\t\t\treturn fmt.Errorf(\"agent processing failed: %w\", result.err)\n\t\t\t}\n\t\t\treturn nil\n\n\t\tcase event := <-messageEvents:\n\t\t\tmsg := event.Payload\n\t\t\tif msg.SessionID == sess.ID && msg.Role == message.Assistant && len(msg.Parts) > 0 {\n\t\t\t\tstopSpinner()\n\n\t\t\t\tcontent := msg.Content().String()\n\t\t\t\treadBytes := messageReadBytes[msg.ID]\n\n\t\t\t\tif len(content) < readBytes {\n\t\t\t\t\tslog.Error(\"Non-interactive: message content is shorter than read bytes\", \"message_length\", len(content), \"read_bytes\", readBytes)\n\t\t\t\t\treturn fmt.Errorf(\"message content is shorter than read bytes: %d < %d\", len(content), readBytes)\n\t\t\t\t}\n\n\t\t\t\tpart := content[readBytes:]\n\t\t\t\t// Trim leading whitespace. Sometimes the LLM includes leading","sourceCodeStart":384,"sourceCodeEnd":420,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/app/app.go#L384-L420","documentation":"After the agent stream finishes, RunNonInteractive inspects the response error. Cancellation is treated as success (returns nil), but any other error is wrapped as 'agent processing failed'. This means the agent ran but the conversation/processing stream ended with an error mid-flight (provider error, tool execution abort, stream disconnect).","triggerScenarios":"AgentCoordinator.Run's stream completes with an error that is neither context.Canceled nor agent.ErrRequestCancelled — e.g. provider returned 4xx/5xx mid-stream, a tool call failed fatally, or the connection dropped during generation.","commonSituations":"API quota/rate limits hit during a long generation; provider 500s; network drop mid-stream; a shell tool command failing in a way the agent treats as fatal.","solutions":["Read the wrapped cause in logs; if it's 429/quota, wait or switch provider/model","Retry the run — transient network/provider failures often resolve","Check provider status page if 5xx errors repeat","Reduce prompt/tool load if a specific tool consistently aborts the stream"],"exampleFix":"// before\nout, err := app.RunNonInteractive(ctx, &buf, prompt, \"\", \"\", true, \"\", false)\n// treat any error as fatal\n// after\nif err != nil && strings.Contains(err.Error(), \"429\") {\n\ttime.Sleep(backoff) // retry transient rate limits\n\tout, err = app.RunNonInteractive(ctx, &buf, prompt, \"\", \"\", true, \"\", false)\n}","handlingStrategy":"retry","validationCode":"if !providerReachable(ctx, endpoint) { // ping provider endpoint\n\treturn errors.New(\"provider endpoint unreachable\")\n}","typeGuard":null,"tryCatchPattern":"var lastErr error\nfor i := 0; i < 3; i++ {\n\terr := app.RunNonInteractive(ctx, w, prompt, \"\", \"\", true, \"\", false)\n\tif err == nil {\n\t\treturn nil\n\t}\n\tif errors.Is(err, context.Canceled) {\n\t\treturn nil // intentional cancel is success in non-interactive mode\n\t}\n\tlastErr = err\n\ttime.Sleep(backoff(i))\n}\nreturn lastErr","preventionTips":["Implement bounded retries with backoff for transient provider errors","Monitor quota/rate limits; switch models on repeated 429s","Treat context cancellation as success — crush returns nil for it"],"tags":["go","llm-provider","streaming","transient"],"backgroundTag":"llm-stream-aborted","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}