charmbracelet/crush · warning

tool call was interrupted and did not produce a result, you

Error message

tool call was interrupted and did not produce a result, you may retry this call if the result is still needed

What it means

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.

Source

Thrown at internal/agent/agent.go:1676

// tool_use to be immediately followed by a tool_result; an interrupted
// session can leave orphaned tool_use blocks that permanently lock the
// conversation. Returns the message and true if any synthetic results were
// produced.
func syntheticToolResultsForOrphanedCalls(m message.Message, knownToolResultIDs map[string]struct{}) (fantasy.Message, bool) {
	var syntheticParts []fantasy.MessagePart
	for _, tc := range m.ToolCalls() {
		if _, hasResult := knownToolResultIDs[tc.ID]; hasResult {
			continue
		}
		slog.Warn(
			"Injecting synthetic tool result for orphaned tool call",
			"tool_call_id", tc.ID,
			"tool_name", tc.Name,
		)
		syntheticParts = append(syntheticParts, fantasy.ToolResultPart{
			ToolCallID: tc.ID,
			Output: fantasy.ToolResultOutputContentError{
				Error: errors.New("tool call was interrupted and did not produce a result, you may retry this call if the result is still needed"),
			},
		})
	}
	if len(syntheticParts) == 0 {
		return fantasy.Message{}, false
	}
	return fantasy.Message{
		Role:    fantasy.MessageRoleTool,
		Content: syntheticParts,
	}, true
}

func (a *sessionAgent) getSessionMessages(ctx context.Context, session session.Session) ([]message.Message, error) {
	msgs, err := a.messages.List(ctx, session.ID)
	if err != nil {
		return nil, fmt.Errorf("failed to list messages: %w", err)
	}

View on GitHub (pinned to 7944b8e522)

Solutions

  1. Retry the tool call if its result is still needed — the error explicitly states the call may be retried.
  2. Check whether the interruption was intentional (user abort); if so, rephrase or continue without that tool result.
  3. If this happens repeatedly, inspect why the run context is being canceled early (timeout settings, network drops, TUI shutdown logic).

Example fix

// Not a code bug — this is a runtime interruption marker.
// If you control the calling loop:
// before
if err == context.Canceled { return err } // losing the partial turn
// after
result, err := agent.Run(ctx, ...)
if errors.Is(err, context.Canceled) {
    // resume/retry the interrupted tool call in a fresh run
    result, err = agent.Run(newCtx, ...)
}
Defensive patterns

Strategy: retry

Try / catch

result, err := agent.Run(ctx, ...)
if err != nil && errors.Is(err, context.Canceled) {
    // interrupted turn: tool calls were closed with synthetic errors;
    // re-issue only the tool calls whose results are still needed
    return agent.Run(context.Background(), sessionID, "continue")
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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