shareAI-lab/learn-claude-code · error · WorkflowInputError

resume args do not match the original run

Error message

resume args do not match the original run

What it means

On resume, _call_locked() compares caller-supplied args against the args persisted in the run snapshot: passing args=None adopts the saved args, but passing explicit args that differ raises WorkflowInputError('resume args do not match the original run'). Journal cache keys are derived from prompts built from args, so changed args would make replay ambiguous.

Source

Thrown at s16_workflow_runtime/code.py:564

        if resuming:
            run_id = validate_run_id(resume_from_run_id)
        else:
            run_id = reserve_run_id(meta)
        with workflow_run_lock(run_id):
            return await self._call_locked(
                meta, script_fn, args, run_id, resuming
            )

    async def _call_locked(self, meta, script_fn, args, run_id, resuming):
        if resuming:
            snapshot = _read_snapshot(run_id)
            if snapshot.get("workflowName") != meta["name"]:
                raise WorkflowInputError("resume runId does not match workflow meta")
            saved_args = snapshot.get("args", {})
            if args is None:
                args = saved_args
            elif args != saved_args:
                raise WorkflowInputError("resume args do not match the original run")
            journal = WorkflowJournal(run_id, resume=True)
        else:
            args = args or {}
            journal = WorkflowJournal(run_id, resume=False)
        task_id = create_task_id(run_id)

        task = LocalWorkflowTask(task_id, run_id, meta)
        # Record the launch envelope before workflow execution starts.
        launched = {"status": "async_launched", "taskId": task_id,
                    "taskType": "local_workflow", "runId": run_id,
                    "workflowName": meta["name"]}
        task.event("async_launched", runId=run_id, taskId=task_id)
        task.event("task_started", workflow=meta["name"],
                   phases=",".join(meta.get("phases", [])) or "-",
                   resume=resuming)
        _write_json(STORE / f"{run_id}.json", {
            "runId": run_id,
            "workflowName": meta["name"],

View on GitHub (pinned to 985456f4ad)

Solutions

  1. Omit args entirely on resume (args=None) to reuse the original run's arguments.
  2. Pass byte-identical args to the original call, including nested values.
  3. If you genuinely need different args, start a new run instead of resuming.

Example fix

# before
await run_workflow("review", args={"changes": diff_v2}, resume_from_run_id=rid)

# after
await run_workflow("review", resume_from_run_id=rid)  # reuses saved args
Defensive patterns

Strategy: validation

Validate before calling

from s16_workflow_runtime import _read_snapshot
saved = _read_snapshot(run_id)["args"]
if args is not None and args != saved:
    raise ValueError("pass args=None or the exact original args")
await run_workflow(name, resume_from_run_id=run_id, args=args)

Try / catch

try:
    await run_workflow(name, args=args, resume_from_run_id=rid)
except WorkflowInputError as e:
    if "args do not match" in str(e):
        return await run_workflow(name, resume_from_run_id=rid)  # reuse saved args
    raise

Prevention

When it happens

Trigger: run_workflow(name, args={...}, resume_from_run_id=<id>) where the dict differs (even by one key or value) from snapshot['args'] recorded at launch.

Common situations: Tweaking the change description or options between the failed run and the resume attempt; defaults injected client-side that differ from what the original caller sent; key-order-independent dict comparison still failing on nested value changes.

Related errors


AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14). Data as JSON: /api/errors/803d731067232d43. Report an issue: GitHub.