Skip to main content
spend throws one of five errors. The table says whether your function ran. All five carry error.idempotencyKey. The manual calls authorize and charge do not throw. They return a denial as a result and every other failure as { error }.

BudgetExceededError

A limit denied the estimate, and your function did not run. error.blocked names the limit and what would let the same request through: error.blocked also carries limitId, window, remaining, held, and resetsAt, and error.balance holds the budget’s balance at the denial.

BudgetUnavailableError

Unprice could not be reached, or it answered 429 TOO_MANY_REQUESTS or 500 INTERNAL_SERVER_ERROR after the client’s retries. Your function did not run. The authorization may still have committed, so retry with the same estimate, ttlMs, and error.idempotencyKey:
If that retry throws BudgetRecoveryError with reason: "authorization_replayed", the first attempt committed and the work never ran. Run it and settle through error.hold, or release that hold. The default handle denies new work while Unprice is unreachable. In local mode, spending continues within the lease until its deadline.

BudgetRequestError

The API rejected the authorization, for example because the budget does not exist (404), has closed (409), or the request is invalid (400). Your function did not run. error.error holds the API error with its code and requestId.

BudgetRecoveryError

The SDK cannot tell what the work used, so it keeps the hold and hands it to you as error.hold. reason: "work_failed". Your function threw. The original error is error.cause. A provider timeout can follow billable work, so check the provider’s result first. Settle known usage with error.hold.settle({ amount }). Call error.hold.release() only after confirming the usage is zero. reason: "authorization_replayed". An authorization under your own idempotency key replayed, so spend did not run your function again. Recover the provider’s result and settle through the hold. A generated key never leaves the call, so its replay only means the client retried a lost response, and spend runs the function. The idempotency key protects budget accounting. It does not give exactly-once provider execution: use the provider’s own idempotency, or a durable workflow, when the work must survive a crash.

BudgetSettleError

The work succeeded and produced error.value, but the settle failed. The first settle may have committed, so do not rerun the work and do not record that usage.
  • Transport failure (error.cause): retry error.hold.settle({ amount: error.actual }).
  • API error (error.error): usage above the reserved maximum returns 409 and keeps the hold open. Reconcile the provider failure that let the work exceed its maximum. Do not clamp the amount or release the hold to hide the excess.
With the client’s waitUntil, a failed background settle goes to onSettleError instead of throwing. See settle in the background.

API error codes