[KongchangAI]
· 2 min read· 1,166 words

Tool Calls Interrupted? APX's Explicit End Marker

Tool Calls Interrupted? APX's Explicit End Marker

Interrupted tool calls must have an explicit ending in the conversation history to prevent the model from fabricating results.

The article notes that AI Agent tool calls are often interrupted by crashes, timeouts, or user stops, leaving dangling calls with only a request and no result. This half-finished state causes the next turn to inherit ambiguous semantics, inducing the model to fabricate results that never happened and even to continue reasoning based on incomplete actions or misjudge side effects. When repairing chat history, APX follows a strict sequencing rule and appends a "not run: the turn was interrupted" record to every unanswered call. It only states that no tool result was received, without claiming the external action failed, thereby preserving room for safe retries. The article further distinguishes portable project context (APC) from local execution state (APX): the former provides persistent intent, while the latter preserves honest operational facts. During recovery, three questions should be answered separately: whether a tool was requested, whether a result was received, and what check is safe.

Why Interrupted Tool Calls Need an Explicit Ending

An AI Agent may "drop the turn" while calling a tool before getting a result—the browser crashes, the provider times out, the runtime restarts, or the user stops it. Such interruptions are normal, but pretending they never happened is not. When repairing chat history, APX follows a simple rule: an interrupted tool call must have an explicit ending in the conversation history. It does not leave a dangling request; instead, it records "the tool did not run because the turn was interrupted."

rss source: An Interrupted Tool Call Needs an Explicit Ending

This rule may seem minor, but it directly addresses the structural requirement of tool-calling APIs: strict sequencing. When an assistant message requests a tool, a corresponding tool result must follow immediately. If the history contains only a request with no result, the next turn inherits an ambiguous state—did the browser search actually happen? Was the file written? Is it safe to retry? The model should not have to guess, and later readers should not infer "the action succeeded" from an assistant message that "requested an action."

The "strict sequencing" requirement of tool-calling APIs stems from the design constraints of modern large-model function calling protocols: conversation messages are divided into roles such as assistant (which may carry tool_calls) and tool (which carries tool_call_id and the result). When parsing history, servers typically require that every tool_call be followed by a tool message with a matching id; otherwise, they return a 400 error or behavior is undefined. This constraint is not a proprietary implementation of any single vendor but a shared specification of mainstream interfaces such as OpenAI Chat Completions and Anthropic Messages. Its purpose is to let the model understand "requesting an action" and "the action's result" as an indivisible atomic unit. When only half a call remains in the history, the model does not see "unknown" but a syntactically incomplete action that semantically looks like it already happened—this is precisely the source of hallucination risk.

The Real Risks of Dangling Calls

Consider a very short conversation history:

User: Check the deployment dashboard.
Assistant: Calling browser_text({ url: ... })
[process interrupted]
User: What did it find?

Without repair, the last question invites the model to fabricate. A plausible-sounding answer might convincingly summarize the dashboard even though no result ever existed. This is worse than a visible interruption because it turns missing evidence into an assertion.

The "half-finished" state of a tool call also contaminates subsequent reasoning. The model may continue planning based on an action that was never completed, or incorrectly believe that a side effect has occurred. In scenarios requiring idempotency or side effects, this confusion can lead to duplicate writes or missed checks.

"Idempotency" means that performing the same operation once or multiple times produces the same effect on system state. Read-only operations such as reading a dashboard or querying task status are naturally idempotent, and repeated execution has no side effects; operations such as writing files, sending messages, creating orders, or deducting inventory are non-idempotent, and repeated execution may cause duplicate writes or duplicate charges. This is exactly what makes interrupted calls most tricky: from the history alone, it is impossible to determine whether a side effect has already occurred. A sound recovery strategy is to prioritize idempotent checks (for example, query the resource's current state before deciding whether to write) or to introduce an idempotency key so that the external service can deduplicate on its own, rather than retrying blindly. This is also why the article emphasizes that the "not run" record only states that no result was received and does not claim external failure—it returns the responsibility for judging whether a side effect occurred to the caller instead of drawing conclusions on the caller's behalf.

How APX Repairs Structure, Not the Story

APX's runtime history repair logic starts from real user entries, ignores orphaned tool results, and checks whether each assistant tool call has a matching result. When a new non-tool entry appears or the history ends, any unanswered call is closed with an explicit record:

not run: the turn was interrupted

The repaired history becomes:

User: Check the deployment dashboard.
Assistant: Calling browser_text({ url: ... })
Tool: not run: the turn was interrupted
User: What did it find?

This record is deliberately narrow. It does not claim that the action failed in the external world—the request may have reached the external service before the runtime lost contact. It only states that APX has no completed tool result for that turn. This distinction is crucial for retries: the safe next step may be to check the current state, ask the user, or run an idempotent check, rather than automatically "doing it again."

The Boundary Between Portable Context and Runtime State

This repair is runtime work, not a modification to project context. APC maintains the portable contract: AGENTS.md in the repository, .apc/ metadata, Agent definitions, skills, and safe shared guidance. Compatible tools can read this contract from another checkout.

APX is the runtime used day to day. Its chat history, tool-call records, sessions, and interruption markers are local execution state. They must describe what happened in a specific conversation, but they are not part of the portable project contract.

This boundary makes recovery easier to reason about. APC still tells the Agent which project rules and skills apply after a restart; APX separately tells it whether the last tool call produced evidence. One layer provides persistent intent, and the other preserves honest operational facts.

The separation of portable context and local runtime state is essentially the separation of "declarative configuration" and "imperative execution records." The former describes what rules the system should be under (project conventions, Agent definitions, available skills) and can be versioned with the repository and reused across machines; the latter describes what actually happened in a particular execution (message sequence, tool calls, interruption markers) and is session-local and time-sensitive. Mixing execution state into the portable contract would cause the contract to drift with every run, making it impossible for other checkouts to reproduce; conversely, stuffing project rules into runtime state would lose intent after a restart. Each layer has different responsibilities, so during recovery they can separately answer the two orthogonal questions: "which rules currently apply" and "did the last action actually succeed?"

Practical Recovery Habits

When a tool-dependent turn is interrupted, separate three questions:

  1. Was a tool requested?
  2. Did APX receive a matching result?
  3. What check is safe before continuing?

Only the second question has a definitive answer from the repaired history. The third depends on the operation itself: check a read-only dashboard, inspect task status, or ask before repeating a write.

An explicit interruption marker is unremarkable, but it prevents a subtle class of Agent errors. APC keeps project context portable, and APX keeps execution history accountable. The next turn can then continue based on evidence rather than from a gap that looks like success.

#tool calling#AI Agent#APX#interrupted tool call#chat history repair#function calling#idempotency
Share:

Related articles