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

  1. Read the embedded payload error text for the true cause
  2. Re-check provider API key and quota/rate limits
  3. Retry — transient provider 5xx/overloaded errors often clear
  4. 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

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


AI-assisted analysis of charmbracelet/crush@7944b8e522 (2026-08-29). Data as JSON: /api/errors/e0d6a1d885287b68. Report an issue: GitHub.