{"record":{"id":"4d0f3187157c71ae","repo":"charmbracelet/crush","slug":"agent-error-w","errorCode":null,"errorMessage":"agent error: %w","messagePattern":"agent error: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/cmd/run.go","lineNumber":452,"sourceCode":"\t\t}\n\t\t// Attribute the error to our run before treating it as\n\t\t// fatal. Async errors from an unrelated workspace run share\n\t\t// this channel, so a foreign failure must not abort us:\n\t\t//   - if the event carries a RunID, it is the authoritative\n\t\t//     correlator: it must match our run exactly, otherwise it\n\t\t//     belongs to a different request and we ignore it.\n\t\t//   - if the event carries no RunID (older server), fall back\n\t\t//     to SessionID: it must be present and match our session,\n\t\t//     otherwise we ignore it.\n\t\tif e.Payload.RunID != \"\" {\n\t\t\tif e.Payload.RunID != s.runID {\n\t\t\t\treturn false, nil\n\t\t\t}\n\t\t} else if e.Payload.SessionID == \"\" || e.Payload.SessionID != s.sessionID {\n\t\t\treturn false, nil\n\t\t}\n\t\tstop()\n\t\treturn true, fmt.Errorf(\"agent error: %w\", e.Payload.Error)\n\t}\n\treturn false, nil\n}\n\n// waitForAgent polls GetAgentInfo until the agent is ready, with a\n// timeout.\nfunc waitForAgent(ctx context.Context, c *client.Client, wsID string) error {\n\ttimeout := time.After(30 * time.Second)\n\tfor {\n\t\tinfo, err := c.GetAgentInfo(ctx, wsID)\n\t\tif err == nil && info.IsReady {\n\t\t\treturn nil\n\t\t}\n\t\tselect {\n\t\tcase <-timeout:\n\t\t\tif err != nil {\n\t\t\t\treturn fmt.Errorf(\"timeout waiting for agent: %w\", err)\n\t\t\t}","sourceCodeStart":434,"sourceCodeEnd":470,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/cmd/run.go#L434-L470","documentation":"Handler for generic agent error events: when an Error-type event arrives matching this session, the stream stops and wraps the event's error object. Unlike 438 this is a structured error event during the run, not the final RunComplete status.","triggerScenarios":"An error event (Payload of event type Error) is published for this session while runStream.handle() is looping — provider stream failure mid-run, internal agent panic, tool subsystem failure, or MCP tool error escalated to the agent.","commonSituations":"Network drop mid-stream from the LLM provider; MCP server crash during tool call; context cancellation from the user (Ctrl-C) surfacing as an agent error; provider streaming protocol error.","solutions":["Inspect the wrapped e.Payload.Error for the root cause","Retry the run for transient network/stream failures","Disable or fix failing MCP servers if the error mentions a tool","Check context cancellation handling if errors appear after Ctrl-C/timeout"],"exampleFix":"// before: treat cancellation as an error\nreturn true, fmt.Errorf(\"agent error: %w\", e.Payload.Error)\n// after\nif errors.Is(e.Payload.Error, context.Canceled) {\n\treturn true, nil // user cancelled, not a real failure\n}\nreturn true, fmt.Errorf(\"agent error: %w\", e.Payload.Error)","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"stop()\nif errors.Is(e.Payload.Error, context.Canceled) || errors.Is(e.Payload.Error, context.DeadlineExceeded) {\n\treturn true, nil // treat cancellation as normal exit\n}\nreturn true, fmt.Errorf(\"agent error: %w\", e.Payload.Error)","preventionTips":["Use errors.Is on the wrapped payload error to distinguish cancellation from real failures","Check MCP server health before runs that depend on their tools","Log the full payload with verbose logging to capture transient provider stream errors"],"tags":["go","agent","events","llm"],"backgroundTag":"agent-error-event","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}