charmbracelet/crush · error
agent run failed: %s
Error message
agent run failed: %s
What it means
When a RunComplete event arrives for this session and its Payload.Error is non-empty and not a cancellation, the stream stops and surfaces the agent-side failure text verbatim via this error.
Source
Thrown at internal/cmd/run.go:407
// mid-turn on finish.reason == tool_use.
//
// Correlation:
// - if we minted a RunID for this SendMessage, only the
// event whose RunID matches is ours; any other turn
// finishing first on the same session (busy-session
// queue path) must be ignored.
// - if we have no RunID (older server, tests), fall back
// to SessionID matching.
if s.runID != "" {
if e.Payload.RunID != s.runID {
return false, nil
}
} else if e.Payload.SessionID != s.sessionID {
return false, nil
}
stop()
if e.Payload.Error != "" && !e.Payload.Cancelled {
return true, fmt.Errorf("agent run failed: %s", e.Payload.Error)
}
// Reconcile stdout against the authoritative final
// assistant text carried in the event. The pubsub fan-in
// does not serialize publishes across upstream brokers, so
// the final message event may not have reached this loop
// yet; the embedded Text field is the backstop that
// guarantees the full final text always appears on stdout.
if e.Payload.MessageID != "" {
full := e.Payload.Text
readBytes := s.read[e.Payload.MessageID]
if readBytes < len(full) {
tail := full[readBytes:]
if readBytes == 0 {
tail = strings.TrimLeft(tail, " \t")
}
if s.printed || strings.TrimSpace(tail) != "" {
s.printed = true
fmt.Fprint(s.out, tail)View on GitHub (pinned to 7944b8e522)
Solutions
- Read the embedded payload error text for the true cause
- Re-check provider API key and quota/rate limits
- Retry — transient provider 5xx/overloaded errors often clear
- Reduce prompt size or split the task if hitting context/token limits
Example fix
// before: retry immediately with no discrimination
c.SendMessage(ctx, ws.ID, sess.ID, runID, prompt)
// after: retry once on transient provider failure
if err := c.SendMessage(ctx, ws.ID, sess.ID, runID, prompt); err != nil {
if strings.Contains(err.Error(), "overloaded") || strings.Contains(err.Error(), "429") {
time.Sleep(5 * time.Second)
err = c.SendMessage(ctx, ws.ID, sess.ID, runID, prompt)
}
if err != nil {
return fmt.Errorf("failed to send message: %w", err)
}
} Defensive patterns
Strategy: try-catch
Try / catch
if e.Payload.Error != "" && !e.Payload.Cancelled {
err := fmt.Errorf("agent run failed: %s", e.Payload.Error)
if strings.Contains(e.Payload.Error, "rate limit") || strings.Contains(e.Payload.Error, "overloaded") {
// transient: schedule retry with backoff
}
return true, err
} Prevention
- Monitor provider quota and rotate/refresh API keys in CI
- Add retry-with-backoff at the orchestration level for transient provider errors
- Keep prompts within the model's context window to avoid hard failures
When it happens
Trigger: Agent finished a run with an error: LLM provider returned an API error (auth, rate limit, overloaded), tool execution panicked/errored fatally, or the model request failed mid-run. The error string from the event is embedded.
Common situations: Expired/invalid API key; provider 429/5xx during long runs; context window exceeded by a huge prompt; a bash/edit tool returned a fatal error the agent surfaced as a run failure.
Related errors
- agent error: %w
- agent configuration not found
- agent coordinator not initialized
- failed to initialize coder agent: %w
- cannot continue an agent tool session: %s
AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29).
Data as JSON: /api/errors/e0d6a1d885287b68.
Report an issue: GitHub.