claude-code - 💡(How to fix) Fix [Workflow tool] resume cache is unreachable for nontrivial workflows because LLM dispatchers can't transcribe args byte-exactly

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…

The Workflow tool's resume cache (resumeFromRunId) requires byte-identical args across invocations to actually fire. Today args is inline JSON only, which means an LLM dispatcher has to perfectly transcribe the args object across every turn — which doesn't hold up as the args grow. Filing this as a gap; surfacing one possible shape of a fix at the bottom, but the right solution is for the Claude Code team to figure out.

Error Message

the full pipeline re-runs, the failure is invisible (no error, just tokens

Root Cause

Background — the cache mechanic this is in tension with

Empirically (Claude Code 2.1.153), the Workflow tool's resume cache keys each agent() call by the byte content of its prompt (and chains through prior call results). Two prompts that differ by a single character get different cache keys, miss the cache, run live, and — because the keys chain — every downstream call misses too. This is fine and expected when the operator's intent really did change.

Fix Action

Fix / Workaround

Summary

The Workflow tool's resume cache (resumeFromRunId) requires byte-identical args across invocations to actually fire. Today args is inline JSON only, which means an LLM dispatcher has to perfectly transcribe the args object across every turn — which doesn't hold up as the args grow. Filing this as a gap; surfacing one possible shape of a fix at the bottom, but the right solution is for the Claude Code team to figure out.

The problem

Prompts are constructed inside the workflow script from the args global. The args parameter today is inline JSON only — there's no file reference. So the dispatcher — in the common case, the Claude Code main loop itself, i.e. an LLM — has to hand-carry the args object across every turn:

  1. LLM dispatcher reads the prior tool result.
  2. LLM dispatcher transcribes the args object into the new tool call.
  3. LLM dispatcher passes the new args inline.
RAW_BUFFERClick to expand / collapse

Summary

The Workflow tool's resume cache (resumeFromRunId) requires byte-identical args across invocations to actually fire. Today args is inline JSON only, which means an LLM dispatcher has to perfectly transcribe the args object across every turn — which doesn't hold up as the args grow. Filing this as a gap; surfacing one possible shape of a fix at the bottom, but the right solution is for the Claude Code team to figure out.

Background — the cache mechanic this is in tension with

Empirically (Claude Code 2.1.153), the Workflow tool's resume cache keys each agent() call by the byte content of its prompt (and chains through prior call results). Two prompts that differ by a single character get different cache keys, miss the cache, run live, and — because the keys chain — every downstream call misses too. This is fine and expected when the operator's intent really did change.

The problem

Prompts are constructed inside the workflow script from the args global. The args parameter today is inline JSON only — there's no file reference. So the dispatcher — in the common case, the Claude Code main loop itself, i.e. an LLM — has to hand-carry the args object across every turn:

  1. LLM dispatcher reads the prior tool result.
  2. LLM dispatcher transcribes the args object into the new tool call.
  3. LLM dispatcher passes the new args inline.

This is an LLM transcribing structured data into a tool call. For trivially small args (a few short strings) it can luck into byte-exactness. For args of any real size — a multi-paragraph feature request, a JSON object holding prior agent outputs, anything resembling a real workflow's working state — the LLM will predictably:

  • summarize prose fields it perceives as verbose
  • reformat / re-indent JSON
  • normalize quote styles or whitespace
  • "clean up" punctuation, capitalization, or phrasing
  • reorder object keys

None of these are bugs in the dispatcher; they're just what LLMs do when copying text. Every one of them silently breaks the resume cache, for reasons unrelated to anything the operator actually changed. There's no programmatic guarantee that the args between turn 1 and turn 2 are byte-identical on the fields the early prompts depend on, and no signal when they aren't.

The cache works, but it's only reachable for toy-sized args. That's the structural gap.

Why this matters in practice

A common workflow shape is: clarify → plan → (return needs_input to ask the operator a question) → operator answers → resume with the answer. The resume cache is supposed to make this cheap on the unchanged prefix. Today it does, if the dispatcher hits byte-exactness. When the dispatcher is an LLM and the args are nontrivial, byte-exactness is not the realistic outcome — the full pipeline re-runs, the failure is invisible (no error, just tokens burned), and the bigger the workflow's working state grows, the more reliably the cache misses.

Empirical data

A 3-step workflow with each agent's prompt referencing only the specific args fields it needs, model: 'haiku' on all calls:

RunArgsResume?TokensWhat ran
1{featureRequest:"add dark mode toggle to the right pane", plannerAnswers: null}79,923clarify + plan live, returned needs_input
2same featureRequest, plannerAnswers: {color:"midnight blue"}yes39,776clarify + plan cached, integrate live

The cache fired in run 2 because the dispatcher (Claude Code's main loop, an LLM) happened to transcribe the 9-word featureRequest string byte-perfectly. This was a small-enough args that LLM-transcription held by accident. With a realistic feature request — say, 400 lines of accumulated context, or a JSON object holding prior agent outputs — the same dispatcher would have summarized, reformatted, or re-serialized somewhere along the way, and the cache would have silently missed.

Minimal repro workflow available on request.

One shape a fix could take (illustrative — you'll know better)

The core ask is: give the dispatcher some way to refer to the canonical args without having to retype them every turn. The cheapest version of that we could think of is an additional parameter that points at a file on disk — something like argsPath: string — so the dispatcher writes the canonical args once, then passes the path on subsequent invocations. The bytes flowing into the cache layer become deterministic, and the LLM is no longer in the byte-fidelity path at all.

But that's just one shape. There may be a better one — content-addressed args, an "args handle" returned from the tool result, server-side normalization with explicit guarantees, something else entirely. Whichever shape closes the gap so that resume cache becomes reachable for non-toy workflows.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING