Skip to content
Ironflow is pre-1.0: APIs can change between releases. Pin a version. What is ready →

Scoped Injection

Scoped Injection is history editing — change what happened, see what would have happened. Pause a running workflow at step boundaries, inspect and modify completed step outputs, and resume execution with the modified data. It extends history correction (hot patching) from reactive (fix after failure) to proactive (intervene during execution).

  • Debug production issues in real-time without cancelling workflows
  • Modify intermediate data flowing between steps
  • Test “what if” scenarios on live workflows
  • Fix data issues before they propagate to downstream steps

Request the run to pause at the next step boundary:

Terminal window
ironflow run pause run-abc123
# Run run-abc123: pause_requested

The workflow finishes its current step, then holds before the next one.

View completed steps, their outputs, and which step will execute next:

Terminal window
ironflow run paused-state run-abc123

Use --json for machine-readable output:

Terminal window
ironflow run paused-state run-abc123 --json
{
"pause_reason": "injection",
"next_step_hint": "send-notification",
"steps": [
{ "id": "step_001", "name": "fetch-order", "injected": false, "output": {"orderId": "123"} },
{ "id": "step_002", "name": "validate-payment", "injected": false, "output": {"valid": true, "amount": 99} }
]
}

Replace the output of a completed step. The run must already be paused for injection — injecting into a run whose status is not paused, or whose pause reason is something other than injection, is rejected with FailedPrecondition (“run is not paused for injection”).

Terminal window
ironflow run inject run-abc123 step_002 \
--output '{"valid": true, "amount": 50}' \
--reason "Corrected amount for partial refund test"
# Injected step step_002
# Previous output: {"valid":true,"amount":99}

An injection can fail after the step is written. If the run leaves the paused-for-injection state during the write (for example, a concurrent resume), the call fails with the Connect code aborted and the error reason injection_unverified. In the SDKs this is InjectionUnverifiedError in TypeScript and ErrInjectionUnverified in Go. Python has no dedicated error; its IronflowRPCError carries code aborted, which also covers a contended write that wrote nothing. The step may already hold the new output, so inspect it before you retry. Do not treat this error as a rejected injection. A rejection for a run that is not paused for injection (see above) writes nothing.

Resume execution from where it paused:

Terminal window
ironflow run resume run-abc123

Resume from a specific step:

Terminal window
ironflow run resume run-abc123 --from-step step_003

The workflow continues with the injected data flowing to downstream steps.


import { createClient } from "@ironflow/node";
const client = createClient({ serverUrl: "http://localhost:9123" });
// Pause a running workflow
await client.pauseRun("run_abc123");
// Inspect paused state
const state = await client.getPausedState("run_abc123");
for (const step of state.steps) {
console.log(step.name, step.output, step.injected);
}
console.log("Next step:", state.nextStepHint);
// Inject modified output
const result = await client.injectStepOutput(
"run_abc123",
"step_xyz",
{ corrected: true },
"Manual correction"
);
console.log("Previous output:", result.previousOutput);
// Resume
await client.resumeRun("run_abc123");

The run detail page shows scoped injection controls:

  1. Pause — Click the pause button on a running workflow
  2. Inspect — View all completed steps and their outputs in the timeline
  3. Inject — Click a step to edit its output JSON, enter a reason, and save
  4. Resume — Click the resume button to continue execution

Injected steps are visually marked in the timeline so you can see which outputs were modified.


You can also inject corrected output into FAILED steps. This transitions the step to COMPLETED and allows the run to resume past the failure point — a powerful recovery mechanism for production incidents.

Terminal window
ironflow run inject run-abc123 step_003 \
--output '{"recovered": true}' \
--reason "Recovered from external API timeout"

Calling PauseRun on a run that is already pause-requested returns success without error. A waiting run immediately gains an explicit paused hold with the injection reason. Resuming the hold preserves its original wait condition and deadline.


An injection is recorded as a step.injected audit event with:

  • The step ID
  • Previous and new output values
  • The reason provided
  • Timestamp and actor

This is gated on the function’s recording config: with recording: false (the default) the injection still happens, but no audit event is written. Set recording: true on the function if you need the trail. If you set recordingProfile, it must be all or steps. The run_lifecycle profile does not capture step events.

Query injection history via the audit CLI:

Terminal window
ironflow audit trail <run-id> --type step.injected