An operation expired before signing
Why the window is 30 seconds, what expiry costs, and how to restructure a flow that keeps missing it.
The error
{
"error": {
"kind": "invalid_request_error",
"code": "operation_expired",
"message": "The authorization expired before it was signed, create a new operation. Authorizations are short-lived because they pin live chain state.",
"param": null,
"request_id": "req_..."
}
}
The operation moves to expired. Nothing moved, no fee was charged,
and no signature exists. Creating a replacement is safe.
Why 30 seconds
The authorization pins live chain state. On Solana that is a blockhash with a hard on-chain lifetime: a finalized blockhash is already about 35 slots old when we read it, leaving roughly 46 seconds of its 150-slot life. The message cannot be rebuilt once your key has signed it, so the window has to close well before the blockhash does.
The window is a machine budget, not a human one. Decode, rebuild, one KMS call: well under a second.
Common causes
| Cause | Fix |
|---|---|
| A human approval step inside the window | Move the approval before POST /operations. Create the operation only once the decision is made |
| The verifier fetching remote data per send | Cache the constants and the wallet's signing_reference at startup. Nothing in the procedure needs a network call except your KMS |
| A cold-start serverless signer | Keep the signing path warm, or run it as a long-lived service |
| A queue between create and sign | Create the operation from the worker that will sign it, not from an upstream producer |
| A slow KMS region | Co-locate the signing service with the key |
The shape that works
1. Your product decides a send should happen.
2. Any human approval happens here.
3. Worker: POST /operations
4. Same worker, same request: verify, sign, POST /authorize
5. Wait for wallet.operation_confirmed
Steps 3 and 4 belong in one synchronous block. If they can be separated by a queue, a retry, or a deploy, the window will be missed eventually.
If it keeps happening
Instrument the elapsed time between your POST /operations response and
your POST /authorize request. If the median is above a second, the
problem is in your pipeline rather than in the window. Expired operations
cost nothing, so a retry loop that recreates and re-signs is a legitimate
stopgap while you restructure.
See Sending Funds and the Signing Guide.