Reference
Errors
The error document of the Tenrai API, its codes and HTTP statuses, which failures to retry, and the messages a rejected transaction gets.
Error document
Every failure answers with the same JSON document:
{
"api_version": 1,
"code": "unavailable",
"message": "the node is busy; retry shortly",
"retry_after_millis": 250
}| Field | Meaning |
|---|---|
code | Stable category for programs, see below |
message | One line of plain text for people, at most 512 bytes. Do not parse it |
retry_after_millis | Set only when a retry makes sense |
Codes
| HTTP | code | When | Retry |
|---|---|---|---|
| 400 | invalid_request | Invalid input, or a transaction the rules reject | No |
| 403 | invalid_request | A web page whose origin is not admitted | No |
| 404 | not_found | Unknown record | No |
| 409 | conflict | Already pooled, or an input is spent or unknown | No |
| 429 | rate_limited | Too many requests or sockets from one address | After retry_after_millis |
| 503 | unavailable | Busy, still loading, low on disk, or the mempool is full | After retry_after_millis |
| 500 | internal | The service could not answer | No |
Some refusals happen before a route runs and carry no document, only the status:
| HTTP | When |
|---|---|
| 404 | Unknown route |
| 405 | Wrong method |
| 413 | Body over 128 KiB |
| 415 | Body without content-type: application/json |
| 422 | JSON of the wrong shape, an unknown field, or an api_version other than 1 |
The same goes for a 502 or 504 from a proxy in front of the service. Check the status before you parse the body.
Retrying
- Reads: repeat after
429and503, waitingretry_after_millis. A read with no answer may be repeated after a short pause. - Submissions: repeat after
429and503only. After a lost answer, a timeout or a proxy error, askGET /v1/transactions/{id}/statusfirst. The transaction ID is known before you submit. 409on a submission usually means the transaction is already in the mempool. Read its status.
Submitting the same signed transaction twice is harmless: it has one ID and can execute once.
Transaction rejections
| Message | Meaning |
|---|---|
the fee is below the minimum for the transaction mass | Raise the fee to at least minimum_fee_per_mass × mass |
the outputs exceed the inputs | The inputs do not cover outputs plus fee |
an input is not in the finalized UTXO set | 409. The output is spent, or not final yet |
an input is already spent by a pooled transaction | 409. Another pending transaction uses it |
the transaction is already pooled | 409. Nothing to do |
an input authorization is invalid | A signature or key does not match the output's lock |
the transaction is bound to different chain rules | Built for another chain, version or rule set. Read the manifest again |
the transaction validity interval has not opened yet | valid_from lies ahead of the finalized head |
the transaction validity interval has expired | Build and sign it again |
finalized state moved; resubmit the transaction | 503. Send the same bytes again |
the mempool is full; retry with a higher fee later | 503 |
Staking rejections
| Message | Meaning |
|---|---|
the stake is below the minimum delegation … | Stake at least minimum_delegation |
the validator does not take new stake | The validator is not open for stake, for example because it is leaving |
the validator does not exist, the stake position does not exist | Wrong ID |
the validator or position changed since it was read … | Read the record again and rebuild |
On sockets
A refused frame comes back as an error frame that wraps the same document:
{
"type": "error",
"data": {
"id": 5,
"error": {
"api_version": 1,
"code": "invalid_request",
"message": "the transaction topic needs exactly one transaction_id",
"retry_after_millis": null
}
}
}