Inspecting and Replaying Cloud Runs
Once a flow is deployed, its run history lives in Power Automate. Three CLI commands bring it to your machine:
| Command | What it does |
|---|---|
flowforger runs | Lists a flow's recent cloud runs, newest first |
flowforger run-show | Prints one cloud run in the same trace shape as flowforger run --json |
flowforger run --replay-run <runId> | Runs the flow locally with the cloud run's trigger data and recorded connector results |
Replaying is how you reproduce a production failure locally: the actions that call out answer exactly as they did in the cloud, while everything computed in the flow (Composes, variables, conditions) runs again on your machine, where you can change the flow and run it again.
Permissions
These commands read from the Power Automate Flow API, which needs a flow-service token in addition to the Dataverse token:
- With
--auth, the CLI acquires both from the same token cache. Your app registration needs the delegated Microsoft Flow Service permissionUser(App ID7df0a125-d3be-4c96-aa54-591f83ff541c), plus Dynamics CRMuser_impersonationfor the Dataverse side. See App registration. - Without
--auth, pass--flow-token <token>(resourcehttps://service.flow.microsoft.com/) together with--urland--token(orDATAVERSE_TOKEN) for Dataverse.
If the Flow API refuses the token (401 or 403), the CLI says so and exits 2. The usual causes are a missing User permission, or a flow that is not visible to the signed-in user.
Which flow and which environment
All three commands resolve the flow the same way push and diff do:
--id <workflowid>, if given.- The
workflowIdin the file's@Flow({...})decorator (flowforger pullwrites it). - Otherwise the flow name: the
@Flowname of a.ff.tsfile, or--name <flowName>. A name shared by several flows is refused; pass--id.
The Power Platform environment is found by matching your Dataverse URL (from --url or auth.resources.dataverse in flowforger.config.json) against the environments your account can see. Pass --environment-id <guid> to choose it yourself.
runs: list recent runs
flowforger runs --file flow.ff.ts --auth
flowforger runs --file flow.ff.ts --auth --top 50 --status Failed
flowforger runs --id 11111111-1111-1111-1111-111111111111 --auth --json
--top <n>: how many runs to list (default 20).--status <Succeeded|Failed|Cancelled|Running>: only runs with that status.
On a terminal each run is one line: run id, status, start and end time, and the error code and message of a failed run. When piped, or with --json, the output is { workflowId, flowId, environmentId, runs: [{ runId, status, startTime, endTime, trigger, error? }] }. The exit code is 0.
run-show: one run, in the local trace shape
flowforger run-show --run <runId> --file flow.ff.ts --auth
flowforger run-show --run <runId> --file flow.ff.ts --auth --json > cloud-run.json
run-show prints { runId, status, startTime, endTime, error?, trace }. The trace has the same structure as a local run --json: entries { nodeId, name, status, inputs?, outputs?, error?, children?, iterations? }, a scope's or condition's body under children, and a loop's iterations under iterations[].actions, one per item, nested loops included. The nesting comes from the flow's live definition. On a terminal it prints the same human-readable trace as run.
Because the shapes match, the same jq paths work on a local result and a cloud run. The values are the cloud's own:
- A connector action's
inputsare{ host, parameters }. Locally they are the parameters alone. - The trigger's
outputsare{ headers, body }. - An action the run never recorded appears as
Skipped.
The exit code is 0 when the run Succeeded and 1 otherwise. Usage errors, resolution errors and Flow API errors exit 2.
run --replay-run: replay a cloud run locally
# Find the run
flowforger runs --file flow.ff.ts --auth --status Failed
# Replay it
flowforger run flow.ff.ts --auth --replay-run <runId>
--replay-run fetches the run and prepares the local run from it:
- Input. The run's trigger outputs (
{ headers, body }) are the trigger input. Pass--in payload.jsonto use a different one. - Pins. Every connector, HTTP and child-flow action the run recorded is pinned: it does not run locally, and its outputs are the ones the cloud recorded. Inside a loop, each iteration gets the outputs of its own cloud iteration.
- Failures replay as failures. An action that failed in the cloud fails locally with the cloud's code and error, so successors that only run after success are skipped, and a catch scope that runs after failure runs, as they did in the cloud.
- Everything else recomputes. Composes, variables, conditions and data operations run locally on top of the pinned values. Pinned entries carry
mocked: truein the trace.
The CLI prints a one-line summary of what was pinned. The JSON result has replayedRun: { runId, status } in addition to the usual fields.
The flow that runs is your local file. You can change it (add a Compose, fix an expression, rework a condition) and replay the same run again to see the effect.
Safe mode
Replay always runs in safe mode: it behaves like --live with nothing listed. An external action that has no pin stops the run before it, prints the inputs it would have been called with, and exits 3 (stoppedBefore.reason is "live"). This happens for:
- an action you added locally since the run;
- a loop iteration the cloud run did not have (for example, the local data produced more items);
- a branch the cloud run did not take;
- an action whose cloud result could not be pinned (see below).
Nothing reaches a real service unless you allow it.
Changing what is pinned
--live <Action>[,<Action>]unpins those actions: they call out for real, with the tokens--authprovides.--mock <Action>=<file.json>or--mocks <file.json>replaces the pin with your value, at every iteration. Use it to change one answer and keep the rest of the run as recorded. A mock always succeeds.--stop-before <Action>still stops before that action.
What cannot be pinned
- Secure outputs. An action with secure outputs succeeded in the cloud but its outputs are not downloadable. It is not pinned, and the CLI prints a
[WARN]naming it. In safe mode the run stops before it unless you mock it or list it with--live. - Actions the local flow no longer has. A recorded action whose name is not in the local file is ignored, with a
[WARN]. This includes actions you renamed. - Skipped actions. The cloud recorded nothing for them, so there is nothing to replay. They are silently not pinned.
In the web app
The web app offers the same replay from a flow's Run History ("Replay locally"). The run opens in the flow editor with every external action pinned, and a Pins pane lets you pin or unpin actions, per loop iteration, while you debug. See Flow editor.
See also
- Local testing:
--mock,--stop-beforeand--livewithout a cloud run. - Deploying safely:
diffandpush. - CLI reference.