Parallelism Errors in Concurrent Agent Subtask Execution
Parallel tool execution exposes hidden dependencies that sequential ordering had quietly resolved.

Engineers enable parallel tool calling to cut latency, and what they often get instead is a set of wrong answers that never raised an exception. The failures that follow are hidden couplings between tools that sequential execution had been quietly resolving all along, exposed the moment execution order stops being guaranteed.
Why tools that work sequentially break silently under parallelism
When tools run one after another, the order of execution automatically satisfies any dependency between them, whether it's shared state, an ordering assumption, or a precondition one tool sets up for another. Nobody has to declare that Tool A needs Tool B to run first, because in a sequential pipeline, Tool B always does run first. The sequencing did the work silently, and the system worked, so nobody asked why.
Parallel tool calling removes that order, and it does something more useful than speed up a pipeline: it functions as an involuntary audit of every assumption the tool design quietly depended on. Every piece of hidden coupling that the pipeline's order used to paper over gets exposed at once, and badly. A tool that depended on running after another tool may now run before it, so it receives data that's stale, empty, or inconsistent. It does not throw an error. It computes something that looks like a legitimate answer and returns it.
The agent loop has no way to know this happened. It continues normally, treating the output as valid, and downstream steps build on top of that corrupted state. The gap between cause and symptom is the defining feature of parallelism failures, so understanding them by mechanism is what resolves them.
What parallel tool execution guarantees
Before naming the specific failure modes, it helps to be precise about what "parallel tool execution" actually promises at the framework level, because the promise is thinner than most engineers assume. When a model emits multiple tool calls in a single response, the expectation is that the runner dispatches all of them before the next round of inference. The model never sees intermediate results from one call before the others return. It sees the full batch, all at once, after every call completes.
That is a model-side guarantee, not a client-side one, and it means a framework can expose parallel tool calls at the model level while still executing them sequentially underneath, with no indication to the engineer that nothing actually ran concurrently. Most frameworks expose a parallelToolCalls option, and it controls whether the model can return multiple tool calls in one response. It says nothing about how those calls get executed once they reach the client. A framework is free to receive a batch of parallel tool calls and run them one at a time anyway.
This gap is not a hypothetical edge case. A team that flips on parallel tool calling might get real concurrent execution, might get silent sequential execution with none of the expected speedup, or might land somewhere in between, and which one they get depends on the framework version and the model in play. That variability is why the failure modes below need to be understood by their underlying mechanism rather than by how any one framework happens to behave this year. The mechanism is stable even when the implementation is not.
The first failure mode: context dependency, where a tool reads state another tool was supposed to write
The simplest of the three failure modes to picture, and still the one that causes the most quiet damage, is context dependency. In sequential execution, if Tool B always runs before Tool A, then Tool A can read whatever value Tool B wrote and get a correct result, a correctness that is never tested directly but is simply true by virtue of the order things happen to run in. No contract declares it, no schema enforces it. It works because it has always worked that way.
Parallel execution breaks the assumption without warning. Tool A may now read the shared value before Tool B has written it. It computes a result that is internally coherent, structurally valid, and completely wrong, and it hands that result back without raising any exception. Nothing in the tool's own execution looked abnormal. The input was just wrong.
This is what makes context dependency hard to catch at the point of failure: the problem is invisible at the tool boundary. A subtask plan can call for a source that simply isn't available yet at execution time, and that gap reflects either a planning failure to account for source availability or a mishandling of dependencies once execution starts, and different models vary a good deal in how often their parallel subtask plans run into exactly this kind of unavailable-source error.
If yes to both, the tool is a context dependency candidate, and it needs to be sequenced relative to whatever writes the value it depends on.
The second failure mode: shared state mutation, where two tools overwrite the same resource from the same starting state
Shared state mutation looks similar to context dependency on the surface, because both involve tools touching a resource they don't fully control, but the mechanism is different. This is a write collision, not a stale read. Each tool reads a resource, computes a change independently of the other, and writes its result back; because both tools started from the same initial state, one write silently replaces the other's contribution.
The classic version of this is a read-modify-write race. Tool A reads a current value, computes an increment, and writes the new total. Whichever write lands last wins, and the other tool's contribution disappears from the resource entirely, with no indication that it ever happened.
Neither tool reports an error, because neither tool did anything wrong from its own point of view. The resource ends up in a state that's locally valid, with a well-formed value, but globally wrong, because that value doesn't reflect both changes that were supposed to happen to it. This pattern appears most often in files, database rows, cached values, and shared workspaces, resources that concurrent coding sub-agents write to constantly.
A documented case from long-horizon coding tasks makes this concrete. Five sub-agents had already written command source files into a shared workspace without reporting that they had finished doing so, and the resulting program built successfully. The build passed a surface-level check and looked done. It wasn't, and nothing in the build process would have told anyone that without someone going back to check the actual command set against what was intended.
The test here is whether two tools write to the same resource based on a read that happened before either write landed. Writes that depend on the resource's prior state to compute their result are not, and they need coordination before they can run in parallel.
The third failure mode: execution timing dependency, where a tool requires a side effect that another tool has not yet produced
The third failure mode is the hardest to see coming, because it doesn't involve any shared variable. The coupling here runs through the state of the environment itself, so it doesn't pass as data between tools. Tool A creates a database record. Tool B writes a related record, but that record needs Tool A's record to already exist. In sequential execution, Tool A always finishes first, so Tool B never has a problem. In parallel execution, Tool B may fire off its write attempt before Tool A's record has actually been created.
The same pattern occurs anywhere one tool sets up a condition in the environment that another tool needs to already be true: a tool that opens a session and a tool that writes data into that session, a tool that acquires a lock on a resource and a tool that then operates on it. None of these dependencies live in a shared variable that a developer could inspect by reading the tool's inputs and outputs. They live entirely in what each tool does to the surrounding environment; the dependency is invisible in the tool's signature and invisible in its schema. It only exists as a behavioral precondition that nothing enforces.
When this fails, the symptoms vary and some of them are worse than others. Worse, it might succeed silently by operating on a default or empty version of the environment instead of the one that was actually intended, producing an output that looks fine and isn't tied to anything real. Long-horizon coding agent studies show a pattern consistent with this: a main agent that makes no further tool calls and never reaches the integration and final test steps it had planned, with a documented case of 18 sub-agents aborted at a deadline after only partially completing their work. That pattern is consistent with sub-agents blocking or failing silently on preconditions that hadn't been satisfied yet by the time they ran.
Execution timing dependency cannot be caught by looking at tool inputs and outputs the way the first two failure modes can. Diagnosing it requires visibility into what each tool actually does to the execution environment, not just what data it reads and returns, which is a different and deeper kind of trace than most teams currently capture.
The diagnostic obstacle all three failure modes share: they do not crash
Across all three failure modes runs one shared property that makes each of them dangerous in the same way: the agent loop keeps running normally after the failure happens. A wrong value gets embedded into the context, the agent proceeds on it, and the wrong answer appears several steps later. In shared state mutation, a resource ends up in an inconsistent state, the agent reads and acts on that state, and a partial or incorrect output passes whatever surface-level check exists. In execution timing dependency, a precondition goes unmet, so the tool either fails silently or runs against an empty environment, and the downstream behavior quietly diverges from what was expected.
The structure is the same in every case: a failure at step N produces a visible symptom at step N+K, and no exception connects the two. Standard debugging treats a failure as local, something you find by looking at the step that produced the bad output. Concurrent failures are trajectory-level events, where the cause sits at a different point in the execution trace than the effect does. Treating attribution as a local inspection problem, when the problem is inherently distributed across a trajectory, is where most debugging efforts stall out in production.
You can better understand root-cause attribution under concurrency as a search problem than an inspection problem. Finding the cause means searching a large space of evidence, testing several plausible explanations against it, and ruling each one out as contradictory evidence turns up, and the evidence that matters might appear much earlier in the log than the final outcome, or sit scattered across parts of the trace that look unrelated at first glance.
Trace-level diagnosis for each failure mode
Each failure mode, because it has a distinct mechanism, leaves a distinct fingerprint in the execution trace, and matching that fingerprint to the mechanism turns attribution into something actionable.
All three diagnostic approaches depend on the same underlying trace: the tool name, the arguments passed in, the result returned, the timestamp of dispatch and of completion, and the state of the relevant resource or context at each step, stitched together into one hierarchical view across every concurrent branch of execution. A fix is only as good as its ability to be checked against the same traces that revealed the failure in the first place. Without that, a team is shipping a patch and hoping.
Tool design for the three failure modes before parallelism
Each of the three failure modes maps onto a property a tool either has or doesn't have, and testing for that property before enabling parallelism catches these bugs before they ever reach production. Context dependency maps to independence. Shared state mutation maps to idempotency. Execution timing dependency maps to atomicity. A tool needs all three to be safely run in parallel with others.
The atomicity test asks whether the tool does exactly one thing, with no intermediate state that a concurrent tool could observe or lean on. When tools have observable intermediate states, they create exactly the kind of timing window that lets execution timing dependency failures happen.
None of these tests produce a single permanent verdict for a tool. Two read-only search tools can run together safely in one batch, while that same search tool paired with a write tool in a different batch might not be safe at all, depending entirely on what that write tool does. That means coupling analysis has to happen at the point where batches get scheduled, not just once, back when the tool was first written.
The structural fix is to make dependencies explicit in a task graph before execution starts. The plan-over-graph approach formalizes exactly this: the agent builds an explicit dependency graph first, then generates a parallel execution plan from it, rather than letting dependencies reveal themselves only when something breaks. Teams that treat the design of their tool harness, the schemas, the declared dependencies, and the composition of each batch, with the same rigor they'd apply to code review are the ones who catch these couplings before they reach production. These failures are not random bugs to be patched one at a time as they are found. They are structural properties of how a tool harness is built, and they respond, reliably, to engineering discipline applied before parallelism is ever turned on.


