Skip to main content
A paid request can be interrupted after your payment left: the connection drops, a proxy times out, or Selo holds a settlement it could not confirm. The selo CLI never guesses. It records the attempt, refuses to pay again for that release, and lets you fetch the result with --resume, which reuses the same preflight and Idempotency-Key so Selo never settles a second time.

The run state file

Before it sends the paid request, selo test writes the attempt to .selo/runs/<release>.json, relative to the directory you ran pnpm selo in. It writes the file again with the outcome. The directory is created with mode 700 and the file with mode 600, and each write replaces the file atomically.
.selo/runs/<release>.json
The run state holds the private report link. Never commit or upload .selo/. Add it to your .gitignore.

Phases

While the phase is paying or unresolved, every new selo test for that release stops before the preflight, exits 2 and prints how to recover:
Output
A file that cannot be read or parsed also stops the run with exit 2: the run state … is unreadable; inspect it before paying again.

What leaves a run unresolved

When Selo holds an inbound settlement it could not confirm, it never runs the job and it tells the client not to pay again. A retry of the same request is answered from the held job, without settling again.

Resume a run

1

Look at the payment on chain

Open https://allo.info/tx/<expectedInboundTxId>, or check the payer account on any Algorand explorer. If the transaction is there, Selo was paid.
2

Rerun the same command with --resume

Use the same --api, --url, --method, --network and payer wallet as the run you resume. Pass every other flag as before.
Terminal
3

Read the result

When the job has a report, the output is the same as a normal run: the checks, both transaction ids, the gate and the exit code. The run state moves to concluded.

Why a resume cannot charge you twice

A resume does not run a new preflight. It reads preflightId and idempotencyKey from the run state and sends the paid request again with the same key. Selo’s 402 still requires a payment on that request, so the CLI signs one, but Selo uses the key only to find the job:
  1. Selo verifies the payment with the facilitator. Verifying does not move funds.
  2. Selo claims the job by the preflight id and the Idempotency-Key. The original request already claimed it, so this is a duplicate.
  3. For a duplicate, Selo answers from the existing job and never settles the new payment: the report if it is written, 409 JOB_IN_PROGRESS if it is still running, 409 JOB_TERMINAL if it ended without a report, or 502 INBOUND_SETTLEMENT_UNRESOLVED if its settlement is held.
Selo’s own test suite covers this: “A21: the same Idempotency-Key with two different payments settles once”, and in the CLI, “recovers an interrupted paid call by resuming, settling only once”. A new run without --resume would take a new preflight, derive a new key and be a new paid test. That is why the CLI refuses it while an outcome is unconfirmed.

What a resume can answer

Refusals

Limits

Selo checks the preflight before anything else on the paid route, and a preflight is valid for 15 minutes. After that, the unpaid request of a resume is answered 409 PREFLIGHT_EXPIRED, the CLI cannot fetch the result, and the run stays unresolved. Use the expected transaction id to learn on chain whether Selo was paid.
The refusal to pay again lives only in .selo/runs/<release>.json in the directory you ran from. Another directory, another machine, or a CI runner does not see it. In GitHub Actions the runner’s workspace is not kept between runs, so a re-run of the job starts a new paid test; the action warns you not to re-run blindly. See Gate deploys in GitHub Actions.
The CLI has no command to clear a run. While the file says paying or unresolved, it refuses every new paid run for that release id. Once you have checked on chain what happened to the expected transaction, you can test under a new --release id, or move the run state file aside yourself. Either way the next run is a new paid test.

Next

Held payments and reconciliation

What Selo does on its side with a payment it could not confirm.

Output files

Every field of the run state, the summary and the attestation.