Workflow engine recovery
Diagnose and recover durable work in the merged DALP backend without clearing workflow history or creating duplicate chain operations.
Use this guide when durable work stops advancing in DALP. The Workflow Engine runs inside the same backend workload as the Platform API and Ledger Index. Recovery therefore starts with backend health and persisted transaction state, not with a separate workflow service or deployment registry.
The safe order is inspect, reconcile, repair, and resume. Never delete durable history or send a replacement transaction only because an HTTP request timed out or an indexed view is stale.
Current recovery model
Durable execution persists workflow, activity, and keyed-entity state in PostgreSQL. Healthy backend replicas share ownership. If one replica exits, another can reclaim its shards and continue from persisted state. Recovery does not require service registration, stale-deployment cleanup, or a virtual-object reset.
Prerequisites
| Requirement | Value |
|---|---|
| API access | DALP Platform API for the affected environment |
| Permission | Read access for status checks; transaction administration permission for a force retry |
| Identifier | Transaction request ID, workflow-specific deployment ID, or the operation's idempotency key |
| Chain context | Expected chain, sender, transaction hash when available, and the intended business operation |
| Evidence | Request ID, timestamps, Platform Status snapshot, relevant logs and traces, and the last known transaction state |
Set the example variables:
export DALP_API_URL="https://platform.example.com"
export DALP_API_KEY="sm_dalp_operator_1234567890"
export TRANSACTION_ID="01934567-89ab-7def-8123-456789abcdef"Quickstart: inspect one durable transaction
Read the transaction before taking any recovery action:
curl -sS "$DALP_API_URL/api/v2/transaction-requests/$TRANSACTION_ID" \
-H "X-Api-Key: $DALP_API_KEY"{
"data": {
"transactionId": "01934567-89ab-7def-8123-456789abcdef",
"kind": "token.mint",
"status": "FAILED",
"subStatus": "UNKNOWN_ERROR",
"transactionHash": null,
"blockNumber": null,
"errorMessage": "The signer dependency was unavailable.",
"createdAt": "2026-08-01T08:00:00.000Z",
"updatedAt": "2026-08-01T08:01:00.000Z"
}
}This response is only the starting point. Before retrying a failed or dead-letter entry, check whether a transaction hash, nonce reservation, custody approval, signed payload, or provider-side operation already exists.
Decide from the persisted state
| State or signal | Meaning | Operator response |
|---|---|---|
QUEUED, PENDING_APPROVAL, SIGNING, BROADCASTING, or CONFIRMING | The operation is active or waiting on a dependency | Repair the dependency and keep observing the same request |
COMPLETED | DALP recorded a successful terminal outcome after DRAINED | Do not retry; verify the receipt. Missing indexed state is a producer/listener invariant defect |
FAILED | The operation reached a terminal failure | Reconcile chain and provider side effects, then retry only if the failure is safe to repeat |
DEAD_LETTER | Automatic recovery stopped and operator review is required | Inspect the recorded cause and all external side effects before using an admin route |
CANCELLED | DALP accepted cancellation as the terminal outcome | Confirm whether cancellation happened before or after broadcast; do not force retry by default |
| Receipt exists but the Ledger Index lags | Chain execution and read visibility are at different stages | Wait for index progress; do not resubmit the write |
| An active workflow exists when force retry is requested | The original durable attempt still owns the operation | DALP rejects the retry; wait or investigate the active attempt |
Repair by failure domain
| Failure domain | Evidence to inspect | Recovery |
|---|---|---|
| Backend replica | Readiness, restart reason, runner health, current ownership | Replace the unhealthy replica and confirm another healthy replica reclaims work |
| PostgreSQL | Connection health, direct-session capacity, locks, storage latency | Restore database access and allow persisted work to continue |
| EVM RPC | Upstream health, chain head, nonce, receipt, rate limits | Restore a healthy upstream, then reconcile before any replacement broadcast |
| Signer or custody path | Approval state, signature request, provider correlation ID | Restore access or complete the approval; retain the same DALP operation |
| External provider | Provider status and recorded request identity | Confirm whether the provider accepted the original request before retrying |
| Ledger Index | Chain owner, checkpoint, block lag, backfill, reindex state | Restore RPC or ownership and let the replacement owner resume from its checkpoint |
| Application input or policy | Typed error code, why, fix, authorization, contract revert | Correct the request or governed state; infrastructure retry cannot repair a terminal domain failure |
Force retry a failed transaction
The transaction administration route accepts only FAILED or DEAD_LETTER entries. It creates a fresh native workflow attempt and records the state transition in the transaction audit trail. DALP rejects the request when another attempt is active.
Before forcing a retry, export the failed transaction's signed payload, transaction hash, receipt, nonce, gas price, and failure details as incident evidence. A successful force retry clears attempt-specific execution fields from the transaction row before the new attempt starts; the audit trail records the transition and override metadata, not a copy of those cleared fields.
curl -sS -X POST "$DALP_API_URL/api/v2/transaction-requests/$TRANSACTION_ID/retries" \
-H "X-Api-Key: $DALP_API_KEY" \
-H "Content-Type: application/json" \
-d '{}'{
"data": {
"transactionId": "01934567-89ab-7def-8123-456789abcdef",
"previousStatus": "FAILED",
"status": "QUEUED"
}
}An operator can supply a gas-price or nonce override only when incident evidence justifies it. Those fields change chain execution behavior and require nonce and replacement-transaction reconciliation before use.
After the route returns, poll the original transaction ID. The transaction row remains the stable operator-facing identity even though DALP starts a new durable execution attempt.
Workflow-specific recovery
Not every workflow is a transaction queue entry. Organization deployment, invitation acceptance, identity recovery, settlement, and other long-running operations publish their own typed status or retry surface. Use that domain route and its stable deployment or operation ID.
The same rules still apply:
- Read the domain status and the related transaction requests.
- Distinguish an active wait from a terminal failure.
- Repair the failed dependency before retrying.
- Reuse the documented idempotency or deployment identity.
- Confirm both the durable outcome and indexed visibility.
Do not invent a generic workflow reset when the owning domain exposes no recovery route. Preserve the evidence and escalate the typed failure.
Replica restart and shutdown
A backend restart is a workload recovery operation, not a workflow reset. Readiness removes the replica from traffic. Shutdown drains HTTP, releases runner and Ledger Index ownership, and flushes telemetry. Persisted work remains in PostgreSQL and can move to another healthy replica.
If every backend replica is unavailable, durable work pauses. Restore the merged backend and its direct PostgreSQL path. There is no independent workflow deployment to register first.
Legacy identifiers
Some frozen API error codes and debug-bundle category names retain legacy prefixes for compatibility. Treat those strings as stable wire identifiers. They do not indicate that the retired workflow runtime is still a deployed production dependency.
Close the incident
Record evidence for each boundary before closing recovery:
- backend readiness and healthy durable ownership;
- transaction request state and all known transaction hashes;
- receipt and nonce reconciliation;
- signer, custody, or provider outcome where involved;
- Ledger Index checkpoint and read visibility;
- logs and traces for the recovered attempt;
- the operator decision that made retry safe.
Related operations
- Transaction tracking for queue state, receipts, finality, and read visibility
- Transaction queue lifecycle for the force-retry API contract
- Platform status for backend and dependency health
- Blockchain monitoring for RPC, Ledger Index, and reindex signals
- Workflow Engine for durability, ownership, and failure semantics