- Debug & Observe
- Scoped Injection
Scoped Injection
History Editing (Scoped Injection)
Section titled “History Editing (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).
When to Use
Section titled “When to Use”- 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
CLI Walkthrough
Section titled “CLI Walkthrough”1. Pause a Running Workflow
Section titled “1. Pause a Running Workflow”Request the run to pause at the next step boundary:
ironflow run pause run-abc123# Run run-abc123: pause_requestedThe workflow finishes its current step, then holds before the next one.
2. Inspect Paused State
Section titled “2. Inspect Paused State”View completed steps, their outputs, and which step will execute next:
ironflow run paused-state run-abc123Use --json for machine-readable output:
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} } ]}3. Inject Modified Output
Section titled “3. Inject Modified Output”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”).
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.
4. Resume the Workflow
Section titled “4. Resume the Workflow”Resume execution from where it paused:
ironflow run resume run-abc123Resume from a specific step:
ironflow run resume run-abc123 --from-step step_003The workflow continues with the injected data flowing to downstream steps.
SDK Configuration
Section titled “SDK Configuration”Client Methods
Section titled “Client Methods”import { createClient } from "@ironflow/node";
const client = createClient({ serverUrl: "http://localhost:9123" });
// Pause a running workflowawait client.pauseRun("run_abc123");
// Inspect paused stateconst 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 outputconst result = await client.injectStepOutput( "run_abc123", "step_xyz", { corrected: true }, "Manual correction");console.log("Previous output:", result.previousOutput);
// Resumeawait client.resumeRun("run_abc123");client := ironflow.NewClient(ironflow.ClientConfig{ServerURL: "http://localhost:9123"})
// Pause a running workflowstatus, err := client.PauseRun(ctx, "run_abc123")
// Inspect paused statestate, err := client.GetPausedState(ctx, "run_abc123")for _, step := range state.Steps { fmt.Printf("Step %s: injected=%v\n", step.Name, step.Injected)}
// Inject modified outputprevOutput, err := client.InjectStepOutput(ctx, "run_abc123", "step_xyz", json.RawMessage(`{"corrected": true}`), "Manual correction",)
// Resumerun, err := client.ResumeRun(ctx, "run_abc123", "")import json
from ironflow import IronflowRPCfrom ironflow.rpc.v1 import ( GetPausedStateRequest, InjectStepOutputRequest, PauseRunRequest, ResumeRunRequest,)
with IronflowRPC(server_url="http://localhost:9123") as rpc: # Pause a running workflow rpc.runs.pause(PauseRunRequest(run_id="run_abc123"))
# Inspect paused state state = rpc.runs.get_paused_state(GetPausedStateRequest(run_id="run_abc123")) for step in state.steps: print(step.name, step.output, step.injected) print("Next step:", state.next_step_hint)
# Inject modified output — new_output is JSON bytes result = rpc.runs.inject_step_output(InjectStepOutputRequest( run_id="run_abc123", step_id="step_xyz", new_output=json.dumps({"corrected": True}).encode(), reason="Manual correction", )) print("Previous output:", result.previous_output)
# Resume rpc.runs.resume(ResumeRunRequest(run_id="run_abc123"))Dashboard
Section titled “Dashboard”The run detail page shows scoped injection controls:
- Pause — Click the pause button on a running workflow
- Inspect — View all completed steps and their outputs in the timeline
- Inject — Click a step to edit its output JSON, enter a reason, and save
- Resume — Click the resume button to continue execution
Injected steps are visually marked in the timeline so you can see which outputs were modified.
Advanced Usage
Section titled “Advanced Usage”Injecting into Failed Steps
Section titled “Injecting into Failed Steps”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.
ironflow run inject run-abc123 step_003 \ --output '{"recovered": true}' \ --reason "Recovered from external API timeout"Idempotency
Section titled “Idempotency”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.
Audit Trail
Section titled “Audit Trail”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:
ironflow audit trail <run-id> --type step.injectedWhat’s Next?
Section titled “What’s Next?”- Debugging — Hot patching, TUI debugger, VS Code DAP
- Error Handling — Retry policies and non-retryable errors