Skip to content
PrepMint

How it works

Why the agent stopped before it finished

You asked for five things and got one, with a confident summary and no error. An agent loop ends when the model writes a message containing no tool call — a judgement nothing checks against your task. The five ways a run ends early, each fingerprint, and the phrasing that fixes it.

Rohan Surve11 min read

Last reviewed

You asked for five things in one message: update the three call sites, add the migration, run the tests. The agent updated one call site, wrote a confident paragraph about what it had done, and stopped. Nothing errored, nothing timed out, no warning in the transcript. It decided it was finished, four items early, and handed back the turn.

The documentation describes the loop accurately and the hard limits as configuration. What it does not say is the consequence: the thing that ends the loop is a judgement the model makes, and nothing compares that judgement against what you asked for. Once you have seen that, "it stopped halfway" becomes a small set of distinguishable failures with different fingerprints and different fixes.

What actually ends a turn

An agent is a loop with one exit. The model receives your task, the tools and the conversation so far, and replies. If the reply contains a tool call, the harness runs it, appends the result and sends it all back. If it contains no tool call, the loop is over.

That is the entire stop condition, and Anthropic's Agent SDK documentation says so without hedging: "Turns continue until Claude produces output with no tool calls, at which point the loop ends and the final result is delivered." OpenAI's runner is built the same way — "One SDK run is one application-level turn. The runner keeps looping until it reaches a real stopping point:" — and the last item on its list is a final answer with no more tool work.

Underneath, at the API, this is one field: "Every Messages API response includes a stop_reason field that tells you why Claude stopped generating." The value that ends a healthy run is end_turn, documented as Claude finishing naturally. So the exit is not a completion check. It is the absence of a tool call. A model that writes "I've updated the auth module" and calls nothing has ended your run as definitively as one that finished all five items.

"Done" is a judgement, and nothing checks it

The loop is usually drawn as gather context, take action, verify results, repeat until done. Anthropic is candid about who decides each step: "Claude decides what each step requires based on what it learned from the previous step, chaining dozens of actions together and course-correcting along the way." Stopping is one of those steps — generated by the same model, sampled the same way, out of the same context, then rendered as a stop.

Anthropic's best-practices guide names the consequence in the most useful sentence written about agents anywhere: "Claude stops when the work looks done. Without a check it can run, "looks done" is the only signal available, and you become the verification loop: every mistake waits for you to notice it."

Read that as arithmetic. A five-item request is not one stop decision. It is five chances to conclude the work looks done, because after each item the model is asked again whether to call another tool. Nothing in the loop holds your list and ticks items off it. If your fifth item is the one nobody would miss at a glance, it is the one that goes. And because that judgement is sampled like any other generated text, the same task stops in different places on different runs — why the same prompt gives you a different answer, applied to the exit rather than the content.

Fingerprint one: it judged the work complete

The fingerprint is a summary that reads as a completion report: past tense, tidy closing line, no mention of what was not done — not because anything is hidden, but because the omitted items were not in the sentence being generated. The transcript ends with a text-only message. No error, no truncation, no question.

This is the common case and the hardest to see, because it looks like success. The tell is arithmetic, not tone: a strict subset of your deliverables, reported as done. The immediate fix is trivial — name the remaining items and the loop restarts. The durable fix is a phrasing change, and it is the rest of this article.

Fingerprint two: it ran out of room mid-work

Here the stop is mechanical rather than judged, and looks damaged rather than tidy: a reply ending mid-sentence, an unterminated code block, a run with no closing summary. At the API level this is max_tokens. In a harness it is a turn or spend cap — the Agent SDK returns a result whose subtype is error_max_turns or error_max_budget_usd rather than success, with no final text at all.

The distinct fix is to read the termination state rather than the prose — a script that prints whatever text arrived shows a plausible partial answer and no sign anything was cut off. Then raise the cap, or split the task to fit under it.

Fingerprint three: a tool failed and the failure became the answer

An agent that cannot act can still talk, and talking ends a turn. The fingerprint is a reply reporting a fact it could not have obtained — "the tests pass now" with no test output in the transcript — or a hedge that reads like a conclusion: "I wasn't able to run the build, but the change looks correct." A denied permission produces the same shape. Anthropic describes it directly: "When a tool is denied, Claude receives a rejection message as the tool result and typically attempts a different approach or reports that it couldn't proceed." The second half of that sentence is a stop.

The fix is upstream of the model: grant the permission, fix the credentials, repair the server, then re-run. Whether the model reaches for a tool at all is a separate mechanism, and the subject of why your MCP tools are not being called.

Fingerprint four: it asked you a question and is waiting

This is not a stop at all, which is why it wastes the most time. The fingerprint is a final message ending in a question mark, a permission prompt in a pane you are not looking at, or a run "thinking" for ten minutes with no tool output. Nothing is running; something is blocked on you.

The SDK draws the distinction explicitly: "Both trigger your canUseTool callback, which pauses execution until you return a response. This is different from normal conversation turns where Claude finishes and waits for your next message." A pause is recoverable by answering; a finish is not — answering a finished turn starts a new one, with the old one's conclusions already in the history as fact. The preventive fix is to pre-answer the questions: say up front which files it may edit, which commands it may run, and what needs confirmation.

Fingerprint five: the context filled and your list went with it

The fingerprint is a stop that is also a personality change. The agent obeys the last thing you said and ignores the first, re-reads a file it already read, or asks you something you answered forty minutes ago.

At the API level this has its own termination value: "Claude stopped because it reached the model's context window limit." Inside a harness it appears earlier and quieter, as compaction — older history summarised to free space, and your five-item list is exactly the sort of early detail that gets compressed into a clause.

The accumulation itself belongs to what actually uses up your Claude usage limits. The consequence for stopping is narrower: a task statement that lives only in your first message has a shelf life. Restate the remaining items, or keep them somewhere re-injected each request.

Why the same request finishes as a list and stalls as a paragraph

Prose gives the model nothing to check itself against. "Clean up the auth module and make sure everything still works" contains no state: there is no moment in that sentence where the answer to "am I done?" flips from no to yes, so it gets decided by how finished the work feels. A numbered list changes the physics. The items sit in the context as text, so "have I done all of these?" becomes a question with a comparable answer — which is why the same request, retyped as five numbered lines, routinely completes when the paragraph version did not.

OpenAI's prompting guidance points at the same gap from the other side: "Before increasing reasoning effort, check whether the prompt is missing a success criterion, dependency rule, tool-routing rule, or verification loop." The first item on that list is the one that ends runs early, and no amount of extra reasoning supplies it.

Stopping too late is the same mechanism, pointed the other way

An agent that will not stop — re-editing a file it already fixed, refactoring what you did not ask about, adding a test suite to a one-line change — is not a different bug. It is the same judgement resolving the other way, because nothing told it what "enough" was either. Anthropic's SDK docs name the unbounded case for what it is: "Without limits, the loop runs until Claude finishes on its own, which is fine for well-scoped tasks but can run long on open-ended prompts ("improve this codebase"). Setting a budget is a good default for production agents."

The fix is symmetric. A stop condition is a ceiling as much as a floor: "change these three call sites and nothing else" ends a run as reliably as it starts one. Hard caps exist on both sides, and are worth knowing before you lean on one — a Stop hook that keeps refusing to let a turn end is itself overridden, because "Claude Code overrides a Stop hook after it blocks eight times in a row without progress." (Checked 23 August 2026 — treat any such threshold as a figure that moves.)

Write the stop condition, not the task

Describe the state the world should be in when the work is over, and how the agent proves it. Three parts do almost all of it. One measurable end state — a test result, a clean build, an empty queue. A stated check — the command that demonstrates it. The constraints that must survive — what must not change on the way there. "Fix the failing auth tests" becomes "run the auth test suite until it exits zero, without modifying any test file, then paste the final output."

Claude Code's /goal command formalises this and shows what the plain version is missing: "After each turn, a small fast model checks whether the condition holds. If the model judges it not yet met, Claude starts another turn instead of returning control to you." The completion decision moves off the model doing the work — still a judgement, but no longer made by the party with an interest in being finished.

Note the shape even if you never use the feature: a condition worth evaluating is one the agent's own output can demonstrate. If it cannot be seen in the transcript, no evaluator can act on it.

Ask for verification, not for work

The most reliable trick here is a substitution. "Fix the bug" has no checkable end. "Run the tests and paste the output" does — and the second gets the first done as a side effect, because the output cannot be pasted until the tests pass.

Anything that returns a signal the agent can read works: a test suite, a build exit code, a linter, a screenshot compared against a design. Ask for evidence, not assertion. A run ending in real command output can be checked in ten seconds; one ending in "all done, everything works" has to be re-done to be trusted.

It matters most where you cannot see the transcript. A subagent returns only its final message to whoever called it — what a subagent's isolated context actually holds is the mechanism — so one that stopped early hands back a confident summary of incomplete work with nothing behind it to inspect. Delegated work is exactly the work whose stop condition has to be written down.

Deciding, for a run that has not stopped yet

The order worth asking, when an agent hands you less than you asked for:

  1. Is it stopped, or waiting? A question mark or an off-screen prompt is not a failure, and answering costs nothing. Check this first.
  2. Does the ending look tidy or damaged? Truncation and missing summaries mean a limit was hit; a polished completion report means the model judged it done.
  3. Count the deliverables. A confident subset is the commonest failure and the one that never announces itself.
  4. Is there tool output behind the claim? With no test run in the transcript, "the tests pass" was generated, not observed.
  5. Did the task statement survive? In a long session your original list may have been summarised away. Restate it rather than assume it.
  6. Rewrite the request as a checklist before re-running it. Numbered items give the model something to compare itself against; a paragraph gives a feeling.
  7. Name the end state and the check, not the steps. What must be true when this is over, and which command shows it.
  8. Ask for the evidence. Make the last thing the agent does the thing you would have done to verify it.

None of this depends on which model or harness you use. Every agent loop ends when a model decides to write a message with no tool call in it, and the only question that matters is whether you gave that decision something to be measured against. If you did not, you are the completion test — running by hand, after the fact.

Practise this topic

Reading about it is half of it. Answer some questions on the same material — scored instantly, explanation for every one, no login.

Sources

Rohan Surve

Rohan Surve is a Technical Product Manager in Bengaluru with 12 years in enterprise delivery and 0→1 product building. He works with Claude, Claude Code, Cursor and MCP daily, and built PrepMint after noticing how much of what he "knew" about these tools was half-remembered from documentation he'd skimmed once. Every question here is written from working with the tool and checked against the vendor's own docs.

More articles by Rohan Surve

Question banks on this subject