Skip to main content

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:

CommandWhat it does
flowforger runsLists a flow's recent cloud runs, newest first
flowforger run-showPrints 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 permission User (App ID 7df0a125-d3be-4c96-aa54-591f83ff541c), plus Dynamics CRM user_impersonation for the Dataverse side. See App registration.
  • Without --auth, pass --flow-token <token> (resource https://service.flow.microsoft.com/) together with --url and --token (or DATAVERSE_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:

  1. --id <workflowid>, if given.
  2. The workflowId in the file's @Flow({...}) decorator (flowforger pull writes it).
  3. Otherwise the flow name: the @Flow name of a .ff.ts file, 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 inputs are { host, parameters }. Locally they are the parameters alone.
  • The trigger's outputs are { 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.json to 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: true in 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 --auth provides.
  • --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​