Why does a long tool call run twice when I have several workers?
With several workers or replicas, a long agent tool call can be picked up by a second worker while the first is still running it. How to make the second one stop.
What people see
In langgraph issue #7417 a team reports that tool calls longer than about 3 minutes on LangGraph Cloud are sent again from the last checkpoint while the first call is still running. Both finish, with the same arguments, and the work is done two or three times. The thread has more than 80 comments. The shape is general: any system that hands a run to another worker when it thinks the first one is stuck can start the same action twice.
Why a per-process guard is not enough
A check inside one process ("did I already start this?") cannot see what another machine started. The record of "this action has started" has to live in one place that every worker writes to, and starting an action has to be an atomic claim: exactly one worker can win it.
One shared record with Call4Agents
From version 0.3, every worker can point at the same PostgreSQL database. Before an action runs, the worker
writes its intent; a UNIQUE index lets only one worker write it. A second worker that arrives while the action is
still running does not run it: it gets EffectInDoubt. A caller that arrives after it finished gets
the recorded result.
# pip install "call4agents[postgres]"
import os
import call4agents as c4a
c4a.init("postgresql://user:password@db-host/agents", secret=os.environ["C4A_SECRET"].encode())
@c4a.effect(name="research.run_subagent")
def run_subagent(task: str) -> str:
return subagent.invoke(task) # minutes long, costs money
with c4a.run(thread_id): # the same id on every worker that handles this thread
run_subagent("summarise the filings") # a second worker on the same thread does not start it again
Every worker must use the same secret, because an approval given on one worker is checked on another.
What we tested, and what we did not
In tests/test_shared_record.py each machine is a separate process with its own connection to one
PostgreSQL server: 8 machines calling one action at once run it once; a long action still running on one machine
is not started by a second; after a crash on one machine the next one raises EffectInDoubt. CI runs
these on Linux, macOS and Windows. We have not tested real machines on a real network, or LangGraph Cloud itself.
Without a library
Give each logical action a stable id, write it to a shared database with a unique constraint before the action runs, and treat "already claimed but no result" as unknown, not as permission to run again. For outside APIs, also pass that id as an idempotency key.