Skip to content
PortunaPortunaPortunaDocsTestnet

Errors and retries

Errors come with an HTTP status and a JSON body:

{ "error": { "code": "invalid_authorization", "message": "invalid authorization: chain 1, expected 11155111" } }

code is stable: branch on it. message is for people and may change.

StatuscodeMeaningWhat to do
400invalid_jsonThe body is not JSON, has unknown fields, or is over 64 KiB.Fix the request.
400invalid_queryA query parameter of a list is not valid; message says which.Fix the parameter.
401unauthorizedNo API key, an unknown one or a revoked one.Check the key and the environment: a test key works only on the testnet API.
403partner_disabledYour account is disabled.Contact support.
404not_foundNo such sweep or revocation for your account, or no webhook set.Check the id.
409hot_wallet_not_activeThe hot wallet’s delegate is still being deployed, or its deployment failed.Wait for hot_wallet.active; after failed, repeat POST /v1/hot-wallets.
422unknown_chainThe chain is not supported in this environment.Use a chain of the environment.
422unknown_hot_walletThe hot wallet is not registered on this chain.Register it with POST /v1/hot-wallets.
422unknown_tokenThe token is not listed on this chain.Use a listed ticker, its address or native.
422invalid_addressAn address is not a non-zero 0x-address.Fix the address.
422invalid_amountAn amount is not a positive integer in the smallest units.Send a string of digits, or leave amount out to sweep everything.
422invalid_external_idexternal_id is longer than 128 characters.Use a shorter id.
422invalid_authorizationThe authorization is for another chain or delegate, is not signed by the deposit address, or its signature is invalid.See below.
422invalid_urlThe webhook URL is not a public https URL.Use a public https URL.
500internalSomething failed on our side.Retry later with the same external_id.

A disabled account keeps what it already sent on chain: those transactions are seen through. Queued sweeps, revocations and deployments wait, and continue once the account is enabled again. Webhooks keep coming.

message starts withCause
invalid authorization: chain …Signed for another chain, or for chain 0.
invalid authorization: delegates to …Signed for something other than this hot wallet’s delegate (for a revocation: other than the zero address).
invalid authorization: signature …A high s, or r or s out of range.
signed by …Another key signed: a wrong key, a wrong y_parity or a wrong digest.
authorization is required …A revocation came without its authorization.
anything elseA malformed field: address, y_parity, r or s.

More in Signing authorizations.

A request that is accepted can still end badly: a sweep rejected before sending or failed on chain, with a reason, or a hot wallet whose delegate was not deployed, with an error. Those come in webhooks and in GET /v1/sweeps/{id}. Like code, they are stable codes, such as not_upgraded or insufficient_gas_balance, and never the node’s own messages: every one is in Statuses, confirmations and reorgs.

  • Retry network errors, timeouts and 5xx, with a growing pause, and with the same external_id so that a sweep is never queued twice: see Idempotency.
  • Do not retry 4xx as they are: the same request gets the same answer. 409 hot_wallet_not_active is the exception: it passes once the delegate is deployed.

API errors are *portuna.Error with StatusCode, Code and Message; portuna.ErrorCode(err) gives the code, or "" for other errors such as a network failure.

_, _, err := client.CreateSweep(ctx, req)
switch portuna.ErrorCode(err) {
case "":
if err != nil {
// network error or timeout: retry with the same ExternalID
}
case "invalid_authorization":
// sign again
case "hot_wallet_not_active":
// wait for hot_wallet.active
default:
// fix the request
}