{"record":{"id":"eafcf8461166c3fe","repo":"charmbracelet/crush","slug":"tool-call-was-interrupted-and-did-not-produce-a-re","errorCode":null,"errorMessage":"tool call was interrupted and did not produce a result, you may retry this call if the result is still needed","messagePattern":"tool call was interrupted and did not produce a result, you may retry this call if the result is still needed","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"internal/agent/agent.go","lineNumber":1676,"sourceCode":"// tool_use to be immediately followed by a tool_result; an interrupted\n// session can leave orphaned tool_use blocks that permanently lock the\n// conversation. Returns the message and true if any synthetic results were\n// produced.\nfunc syntheticToolResultsForOrphanedCalls(m message.Message, knownToolResultIDs map[string]struct{}) (fantasy.Message, bool) {\n\tvar syntheticParts []fantasy.MessagePart\n\tfor _, tc := range m.ToolCalls() {\n\t\tif _, hasResult := knownToolResultIDs[tc.ID]; hasResult {\n\t\t\tcontinue\n\t\t}\n\t\tslog.Warn(\n\t\t\t\"Injecting synthetic tool result for orphaned tool call\",\n\t\t\t\"tool_call_id\", tc.ID,\n\t\t\t\"tool_name\", tc.Name,\n\t\t)\n\t\tsyntheticParts = append(syntheticParts, fantasy.ToolResultPart{\n\t\t\tToolCallID: tc.ID,\n\t\t\tOutput: fantasy.ToolResultOutputContentError{\n\t\t\t\tError: errors.New(\"tool call was interrupted and did not produce a result, you may retry this call if the result is still needed\"),\n\t\t\t},\n\t\t})\n\t}\n\tif len(syntheticParts) == 0 {\n\t\treturn fantasy.Message{}, false\n\t}\n\treturn fantasy.Message{\n\t\tRole:    fantasy.MessageRoleTool,\n\t\tContent: syntheticParts,\n\t}, true\n}\n\nfunc (a *sessionAgent) getSessionMessages(ctx context.Context, session session.Session) ([]message.Message, error) {\n\tmsgs, err := a.messages.List(ctx, session.ID)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"failed to list messages: %w\", err)\n\t}\n","sourceCodeStart":1658,"sourceCodeEnd":1694,"githubUrl":"https://github.com/charmbracelet/crush/blob/7944b8e52225d8805e31eacbf7ef24856b0dfb7a/internal/agent/agent.go#L1658-L1694","documentation":"In internal/agent/agent.go the session agent builds a synthetic error tool-result for any tool call that was interrupted (context canceled, stream aborted, or agent stopped) before the tool could produce a real result. This synthetic fantasy.ToolResultOutputContentError is fed back to the LLM so the conversation history stays valid for the next provider call. It tells the model (and the user) that the tool call produced no output and may be safely retried.","triggerScenarios":"The agent's LLM loop is interrupted while a tool call is pending: the user presses Ctrl+C/escapes, the context is canceled, the session is stopped, or the client disconnects after the assistant emitted a tool_call but before the tool finished running. When reconstructing the message history, each such incomplete tool call gets this synthetic error result.","commonSituations":"User interrupts a long-running bash command or a slow LSP tool mid-run; a request times out during a big edit; the TUI session is closed while a sub-agent or MCP tool is still executing.","solutions":["Retry the tool call if its result is still needed — the error explicitly states the call may be retried.","Check whether the interruption was intentional (user abort); if so, rephrase or continue without that tool result.","If this happens repeatedly, inspect why the run context is being canceled early (timeout settings, network drops, TUI shutdown logic)."],"exampleFix":"// Not a code bug — this is a runtime interruption marker.\n// If you control the calling loop:\n// before\nif err == context.Canceled { return err } // losing the partial turn\n// after\nresult, err := agent.Run(ctx, ...)\nif errors.Is(err, context.Canceled) {\n    // resume/retry the interrupted tool call in a fresh run\n    result, err = agent.Run(newCtx, ...)\n}","handlingStrategy":"retry","validationCode":null,"typeGuard":null,"tryCatchPattern":"result, err := agent.Run(ctx, ...)\nif err != nil && errors.Is(err, context.Canceled) {\n    // interrupted turn: tool calls were closed with synthetic errors;\n    // re-issue only the tool calls whose results are still needed\n    return agent.Run(context.Background(), sessionID, \"continue\")\n}","preventionTips":["Avoid canceling the run context mid-tool unless the user explicitly aborts.","Set generous timeouts for long-running tools (bash, LSP) to reduce spurious interruptions.","Treat this synthetic error as retryable rather than fatal in your resume logic."],"tags":["tool-call","interruption","agent-loop","retryable"],"backgroundTag":"tool-call-interrupted","analyzedSha":"7944b8e52225d8805e31eacbf7ef24856b0dfb7a","analyzedAt":"2026-08-29T12:48:59.079Z","schemaVersion":2},"datasetVersion":"2026-08-29T17:17:51.833Z"}