A tool call executes at most once per run
A tool call has side effects. One node can be reached twice in a run (by a fan-in, or by a re-ask after a human approved a gated call), and the same batch of calls arrives with it.
The rule
- A run records each tool call it has executed, keyed by the call identifier, and does not invoke a call whose identifier it has already recorded.
- The repeat is reported on a
skippedoutput, and the stored result of its first execution (the tool-role message and whether it succeeded) is re-emitted among the run's tool messages and results and counted in the batch's outcome. - Re-emitting rather than dropping is required: a batch that paused midway never delivered the already-executed call's message downstream, so dropping the repeat would leave a declared call identifier unanswered and invite the model to retry it under a fresh, unguardable one.
- A call is recorded only once it has returned a result, success or a recoverable error, and never before it is invoked, so a call that interrupts the node for human approval is re-asked and executed once approved, rather than looking already-executed and being silently dropped.
- A call that carries no identifier cannot be guarded and always runs.
What it means
A call is recorded as executed only once it has returned — succeeded, or failed in a way the run can recover from — never at the moment it is invoked. A call that pauses the node to ask for approval never reaches that point, so re-asking it after approval invokes it for real rather than finding it already recorded and being dropped.
The repeat itself is never simply discarded, either. Its first result is re-emitted among the batch's tool messages, under the same identifier, because a batch that paused partway through never delivered that message downstream the first time — dropping the repeat would leave a declared call unanswered and push the model to retry it under a fresh identifier nobody can guard.
A call with no identifier cannot be checked against anything already recorded, so it always runs, on every pass.
Example
One call already executed, and the same batch sent again:
{ "tool_calls": [{ "name": "do_thing", "args": [], "tool_call_id": "call_a" }] }["call_a"]The tool ran once across both runs; the repeat's tool message is the stored
first result, under call_a, and the run's outcome is the stored success.
A batch with one call that already ran and one that only paused for approval, resent once approval is granted:
{
"tool_calls": [
{ "name": "do_thing", "args": [], "tool_call_id": "call_a" },
{ "name": "gated_thing", "args": [], "tool_call_id": "call_gate" }
]
}["call_gate"]call_a was recorded on the first pass and is skipped again; call_gate
only paused the node last time, so it was never recorded and runs now.