How to Build a Harness

15. Stop Reasons & When to Loop

stop, toolUse, error, aborted — what each means.

The Anthropic API returns a stop_reason that tells you why the model stopped generating.

File: packages/ai/src/providers/anthropic.ts L934-954

function mapStopReason(reason: string): StopReason {
  switch (reason) {
    case "end_turn":
      return "stop"; // Model finished naturally
    case "max_tokens":
      return "length"; // Hit token limit
    case "tool_use":
      return "toolUse"; // Wants to call tools
    default:
      throw new Error(`Unhandled: ${reason}`);
  }
}

File: packages/ai/src/types.ts L192

export type StopReason = "stop" | "length" | "toolUse" | "error" | "aborted";

The loop decision:

StopReason Action
"toolUse" Execute tools, loop again
"stop" Model is done, break the loop
"length" Hit token limit, might need to continue
"error" Something went wrong, stop
"aborted" User cancelled, stop

File: packages/agent/src/agent-loop.ts L194-198

if (message.stopReason === "error" || message.stopReason === "aborted") {
  await emit({ type: "turn_end", message, toolResults: [] });
  await emit({ type: "agent_end", messages: newMessages });
  return; // Stop the loop
}

const toolCalls = message.content.filter((c) => c.type === "toolCall");
hasMoreToolCalls = toolCalls.length > 0; // Continue if tools requested

Open this chapter inside the full course