Relationships
#1347 Run-context variables (run.id, workflowRunId) undefined in step-input CEL after suspend+resume
Opened by kneel · 7/22/2026· Shipped 7/23/2026
Inside a workflow step's inputs, the run namespace (run.id, run.workflowId, …) and the flat workflowRunId variable resolve correctly on a fresh, single-pass run. But if the run suspends at a manual_approval gate and is later resumed, any step that executes after the gate fails to evaluate its input CEL:
Invalid expression: Unknown variable: run
> 1 | run.id
^workflowRunId fails the same way (Unknown variable: workflowRunId). This contradicts the docs (references/workflow/references/expressions-and-foreach.md, "Workflow Run Context"), which state these variables are available "at step execution time." A resumed step is executing, yet the run context is not repopulated in its CEL evaluation scope.
Steps to reproduce
Minimal, deterministic — two workflows sharing one throwaway command/shell echo model.
swamp model create command/shell repro-runidWorkflow A (fresh, no gate), single step:
inputs: run: "echo RUNID=${{ run.id }} WFRUNID=${{ workflowRunId }}"
swamp workflow run repro-runid-nogate→ SUCCEEDS, logsRUNID=<uuid> WFRUNID=<uuid>(both resolve, equal).Workflow B (identical echo step, but gated):
- name: gate task: { type: manual_approval, prompt: "...", timeout: 3600 } - name: echo-runid # dependsOn gate (succeeded) task: type: model_method modelIdOrName: repro-runid methodName: execute inputs: run: "echo RUNID=${{ run.id }} WFRUNID=${{ workflowRunId }}"
swamp workflow run repro-runid-gate # suspends at gate swamp workflow approve repro-runid-gate gate swamp workflow resume repro-runid-gate→ FAILS on
echo-runid:Step failed: "Invalid expression: Unknown variable: run\n\n> 1 | run.id\n ^"
The only difference between A and B is the suspend/resume across the approval gate.
Expected
run.id / workflowRunId resolve in step inputs on resumed runs exactly as on fresh runs. The post-resume context should be seeded with run-metadata variables — reconstructable from the run record, not "ephemeral data."
Actual
The run namespace and flat workflowRunId are undefined for any step evaluated after resume, so those references throw Unknown variable and fail the step.
Impact
Any workflow that scopes post-approval work to its own run id — a recommended pattern (the docs' own example uses run.id to prevent cross-run data collisions) — cannot use it in exactly the place it matters most: the step gated behind human approval. Real-world hit: a Stripe hourly-payout workflow's createPayout step (gated by manual approval) filtered data.query('... workflowRunId == "' + run.id + '"') and failed every resume with Unknown variable: run, blocking the payout until the run-scoping was replaced with data.latest(...).
Workaround
Source post-gate inputs from data.latest(model, name) / data.query(...) predicates that don't reference the run namespace.
Secondary — docs defect
expressions-and-foreach.md says: "The flat workflowRunId variable is also available (equivalent to run.id) for backward compatibility with data.query() predicates." This reads as if workflowRunId is a top-level CEL variable you can concatenate into a query string — but it throws Unknown variable: workflowRunId in step-input CEL (confirmed even on a fresh run, where run.id works). Docs should clarify it's only meaningful as a record field inside a data.query(...) predicate, and that neither run.* nor workflowRunId survives into a resumed step's CEL.
Environment
- swamp
20260720.142822.0-sha.0caa5556(git sha0caa555) - Platform: linux
- Reproduced with plain local
swamp workflow run/resume(no serve, no third-party extension —command/shellecho only)
Shipped
Click a lifecycle step above to view its details.