Workflow tool: `args` never reaches the script sandbox (all invocation forms — named, scriptPath, resume)

Status Open
Reported on v2.1.226
Maintainer reply None cached
Activity 0 comments · opened Aug 12, 2026

Summary

A workflow script that reads the documented args global fails with its own "args required" guard in every invocation form — the args value passed to the Workflow tool never reaches the script's sandbox context.

Environment

  • Claude Code CLI 2.1.226 and 2.1.227 (observed on both), macOS (darwin 25.6.0)
  • Model: claude-fable-5
  • Script stored under .claude/workflows/ (project scope)

Reproduction

Script begins (after the meta block) with the documented guard pattern:

if (!args || !args.mode || !args.planFiles || !args.planFiles.length) {
  throw new Error('args required: {mode, featureDir, planFiles[], specPath, distilledPath}')
}

All three invocation forms fail identically, in 4–16 ms, before any agent spawns:

  1. Workflow({name: "plan-gate-review", args: {mode: "gate", planFiles: [...], ...}}) — named workflow resolved from .claude/workflows/
  2. Workflow({scriptPath: "<session script copy>", args: {...}}) — fresh launch by path
  3. Workflow({scriptPath: "...", resumeFromRunId: "wf_...", args: {...}}) — the resume form that the failure notification's own recovery text recommends

In forms 2 and 3, args was passed as a proper JSON object (form 1's first attempt used a JSON-encoded string — a documented mistake — but the object forms fail the same way).

Failure output (identical each time):

Error: args required: {mode, featureDir, planFiles[], specPath, distilledPath}
    at <anonymous> (workflow.js:13:9)
    at workflow.js:157:1275
    at runInContext (native)
    at lBp (/$bunfs/root/src/entrypoints/cli.js:6680:9138)

Run IDs from the observing session, if useful for server-side correlation: wf_4e51f736-a79 (two attempts), wf_4c0c123e-3c6.

Expected

Per the tool description, args is "the value passed as Workflow's args input, verbatim (undefined if not provided)" — a script guard on args.mode should pass when an object with mode is supplied.

Actual

The script's sandbox context sees no args (guard throws as if undefined), regardless of invocation form. The tool layer accepts the parameter; the runInContext script environment apparently never receives the global.

Impact

Parameterized/named workflows are effectively unusable: every invocation requires generating a per-use script copy with the argument values embedded as constants, which defeats the .claude/workflows/ registry and the args-based reuse the tool documents (e.g. while (budget.total && ...) patterns are fine, but any args.<field> parameterization is dead).

Workaround

Embed defaults in the script and fall back when args is absent:

const EMBEDDED = { mode: 'gate', /* ... */ }
const A = (typeof args !== 'undefined' && args && args.mode) ? args : EMBEDDED

With that pattern the same script runs to completion (a 14-agent run finished normally), confirming the defect is isolated to args delivery, not script execution.

Secondary (related, minor): diagnostics false-positives on workflow scripts

The session language server parses workflow scripts as plain modules, so the runtime-valid top-level return produces cascading false diagnostics ("'x' is declared but its value is never read" for values used in the return object). Separately, when subagents copy project code into the session scratchpad for sandboxed execution, the main session surfaces spurious missing-import diagnostics from those partial copies. Both are cosmetic but erode trust in the diagnostics stream.

---
🤖 Filed with Claude Code on the operator's instruction.

View original on GitHub ↗