Canton applications deal with a distributed, multi-party ledger. Errors fall into distinct categories, and each category calls for a different recovery strategy. This page covers the error types you will encounter and how to handle them in your backend.Documentation Index
Fetch the complete documentation index at: https://docs.canton.network/llms.txt
Use this file to discover all available pages before exploring further.
Error Categories
Command Rejection
A command is rejected when the Ledger API refuses it before the transaction reaches the synchronizer. Common causes:- INVALID_ARGUMENT — The command payload does not match the template or choice signature (wrong field types, missing required fields)
- NOT_FOUND — The referenced contract ID does not exist or is not visible to the submitting party
- PERMISSION_DENIED — The authenticated party does not have authorization to perform the action
Contention
Contention occurs when two or more commands try to consume the same contract simultaneously. Only one succeeds; the others receive a FAILED_PRECONDITION (orABORTED) error indicating the contract was already archived.
This is normal in multi-party applications. Two users might try to exercise the same choice on the same contract at nearly the same time. Canton’s consistency model ensures only one succeeds.
Timeout
A command may time out if the synchronizer does not process it within the configured deadline. TheStatusRuntimeException will have a DEADLINE_EXCEEDED code. Timeouts can occur during network congestion or when a counterparty’s validator is slow to respond.
A timeout does not mean the command failed. It may have succeeded but the response did not arrive in time. Before retrying, check the completion stream or query PQS to determine whether the command was applied.
Insufficient Traffic
If your validator’s traffic budget is exhausted, command submissions fail with an error indicating insufficient traffic. This is not a transient error — retrying will not help until the traffic budget is replenished (either manually or via auto-top-up). See Canton Coin and Traffic for how to manage traffic credits.Handling Contention
Contention on consuming contracts is the most common error pattern in Canton applications. The standard approach:- Catch the error — Detect
FAILED_PRECONDITIONorABORTEDin the gRPC response - Re-read the contract — Query PQS for the current state. The contract you targeted may have been archived, and a new contract (from the competing transaction) may now be active.
- Resubmit with a new command ID — Build a new command targeting the current contract and submit it with a fresh command ID
When Not to Retry
Do not retry on:- INVALID_ARGUMENT — The command itself is wrong
- PERMISSION_DENIED — Authorization will not change between retries
- Insufficient traffic — The issue is the traffic budget, not the command
Idempotent Command Submission
The Ledger API deduplicates commands by their command ID. If you submit two commands with the same ID, the second is treated as a duplicate and returns the result of the first. Use this to make your backend idempotent:- Generate a deterministic command ID from the operation’s inputs (e.g., hash of the user ID, action type, and a nonce from the frontend)
- If a network failure prevents you from receiving the response, resubmit with the same command ID
- The Ledger API returns the original result instead of executing the command twice
Completion Stream Monitoring
The Ledger API’s completion stream reports the final status of every submitted command. Subscribe to it for reliable outcome tracking:- Successful completions confirm the transaction was committed
- Failed completions include the error code and details
- Missing completions (within the expected window) suggest the command was lost before reaching the synchronizer
CommandService (rather than CommandSubmissionService) waits for the completion internally and returns the result in a single round-trip. If you use the asynchronous CommandSubmissionService instead, you need to monitor completions yourself.
Backend Recovery Patterns
Startup Recovery
When your backend restarts, it may have in-flight commands whose outcomes are unknown. On startup:- Read the completion stream from the last known offset
- Reconcile in-flight commands with their completion status
- Retry any commands that were never submitted (no completion found and no matching contract in PQS)
Circuit Breaker
If the Ledger API becomes unavailable (validator restart, network partition), wrap your submission logic in a circuit breaker. After a configurable number of consecutive failures, stop submitting and surface a service-unavailable error to callers. Periodically probe the Ledger API to detect recovery.Dead Letter Handling
Commands that fail after all retries should be logged with full context (command ID, template, choice, arguments, error) and sent to a dead letter queue or table. This provides an audit trail and enables manual intervention for edge cases.Further Reading
- Backend Development — Ledger API client setup, including error handling examples
- Canton Coin and Traffic — Managing traffic to avoid submission failures
- Observability — Logging and metrics for error tracking