Skip to content
Docs · Start here

Errors & rate limits

Every error is JSON with an error sentence you can show a person, and often a code a program can branch on. What each status and code means, and what to do.

View as Markdown
All topics
On this page

Errors come back as { "error": "…", "code"?: "…", …details }. The sentence says what happened and what to do; the code is stable for programs. Nothing is charged for a refused request; the one exception is an AI model that used its whole token budget without answering (budget_exhausted, see AI Gateway).

Status codes

StatusMeaning
400The request is wrong: a missing field, a bad value, or a SQL mistake (the database's own message, cleaned).
401No credential, or it is invalid, revoked or past its expiry date.
402Money or plan: a plan count or usage allowance reached, the Balance or AI credit too low, or the spending limit reached.
403The credential may not do this: role, read-only key, project key, a paused key, owner-only money action, a suspended or unverified account, or a workspace that requires two-factor sign-in of a member who has not turned it on.
404Not found — or it belongs to another workspace.
409The resource is in the wrong state: still being created, disabled, another deploy of it running, or a delete blocked by money or work in flight.
410A database that no longer exists on the edge, or a resumable upload that expired.
413Too large: an upload over the size cap, or AI input over 200,000 characters.
422Refused on purpose: a suppressed email address, a phone app sent to deploy, or an AI answer that used the whole token budget with nothing to show.
429Too many requests. Wait Retry-After seconds.
501Not available yet (for example, a Postgres database, an image media asset, or a test checkout before test mode is on).
502Something upstream failed: a delivery provider refused, or a delete could not finish (it is safe to retry).
503Temporarily unavailable. Retry after the Retry-After header when there is one.

Codes

CodeStatusMeaning and fields
plan_limit402 / 429A plan count, the email quota, a memory quota, the users of a sign-in pool or the vectors in a collection is reached, or the plan does not include the service (required_plan names the plan that does). limit, current; a memory quota adds limit_name (memory_items_per_store or memory_bytes_per_workspace), and the users or vectors feature (pool_users or vectors_per_collection); the public sign-in endpoints answer pool_users without limit or current. As a 429: more agent runs at once than the plan allows.
usage_limit402Hobby: the plan's file storage, database storage or build minutes are used up, so uploads, writes that add data or deploys are refused. metric, limit, current, required_plan.
usage_paused402A paid plan past its included usage that cannot pay for more: the paying workspace's Balance is empty, or the last charge did not fit that Balance or its monthly spending limit. metric, limit, current, balance_cents (the paying workspace's), add_funds_url, and shared_workspaces when the plan covers several workspaces.
insufficient_balance402The Balance does not cover a purchase. price_usd, balance_usd, shortfall_usd, add_funds_url, card_on_file. In a workspace another workspace's plan covers, the Balance short is that workspace's own, which pays for its domains (balance_scope: "own"): add_funds_url and add_funds_api add to it.
ai_credits_exhausted402Included AI and the Balance are used up. available_usd, resets_on, buy_url.
ai_credits_insufficient402Some AI credit is left, but less than this request's estimate. Lower max_tokens, shorten the prompt, choose a cheaper model or add funds.
ai_request_too_large402One AI request may reserve at most $2 on hobby, $10 on pro, $10 on team, $10 on scale. Shorten it, cap the output or split the work.
spend_limit_reached402The monthly spending limit of the workspace whose Balance pays (on a paid plan, the paying workspace's; for a domain, the buying workspace's own, limit_scope: "own" where another workspace's plan covers it) would be passed. spend_limit_usd, spent_this_month_usd, limits_url.
email_unverified402 / 403The workspace owner's email must be verified (Hobby vendor AI models, live payment links).
payouts_require_owner403Withdrawals, refunds, payout setup and adding or removing the card on file need the owner signed in to the dashboard.
dashboard_only403 / 409Only the person signed in to the dashboard can make this change: changing, cancelling or resuming a paid plan, the billing portal, the spending limit and alerts, adding funds with the card on file, switching auto-reload on, making someone an owner, leaving a workspace and deleting one. An AI connector or another app whose role could make the change gets this instead (one whose role could not is told so), and nothing was changed. dashboard_url is the page to do it on: hand it to the person. As a 409: a checkout link asked for a workspace that already pays for a plan, since changing that plan charges the card on file at once. Every call is listed in Billing → What needs the dashboard.
card_requires_owner402 / 403Paying with the card on file (payWithCard on a domain purchase, renewal or transfer) or letting renewals fall back to it (autoRenewCard, cardBackup) needs the workspace owner signed in to the dashboard. An AI connector or API key gets this instead (403 for cardBackup), and nothing was charged or changed. dashboard_url is the Domains page, where the owner can do it; or pay from the Balance (a purchase, renewal or transfer that asked for the card also carries add_funds_url; in a workspace another workspace's plan covers, that is this workspace's own balance, which pays for its domains, with balance_scope own). See Domains → Who pays, and how.
key_expired401The API key is past the expiry date an owner or admin gave it. Give it a new date, or use a new key.
key_paused403An owner or admin paused the API key. Resuming it in Settings → API access makes it work again.
account_suspended / key_creator_suspended403Harakumo suspended the account making the request, or the account that created the API key. Nothing has been deleted; write to support.
workspace_requires_mfa403The workspace requires two-factor sign-in of every member, and yours is off. Turn it on in Settings → Account; your own account pages stay open. API keys are not affected.
ai_gateway_off / ai_model_off403The AI Gateway, or this model, is switched off for the workspace (Dashboard → AI Gateway).
origin_not_allowed403An auth pool refused a browser Origin that is not on its list.
browser_access_off403An auth pool's browser sign-in is turned off.
mail_paused / mail_suspended403Sending is paused by an admin, or suspended by the platform.
invalid_domain400A sending domain that is not a real domain you could own (a single word, a test domain, a Harakumo name).
invalid_from / invalid_reply_to400A From or Reply-To address that is not allowed; the sentence says which rule.
sender_name / reserved_subject400A sender name or subject that breaks the rules in Mail → Sender names; the sentence says which.
from_domain_not_added400A from address on a domain that is not a sending domain of this workspace. Add it first.
from_not_project_domain403A key limited to one project asked to send from, or to set its project's From address on, a domain that is not its project's own. It sends from its project's sending domain or the shared no-reply; a person moves the project to a new domain on the Mail page.
sender_name_other_project403A key limited to one project asked to send under, or to save as its project's sender name, another project's name or sender name, or a close variant of one. Leave fromName out to use its own project's name.
domain_claimed_elsewhere / domain_taken409The domain belongs to another workspace on Harakumo.
dns_hosting_off409A sending domain bought on Harakumo whose DNS hosting is off. platform_option says how to turn it on.
mail_not_ready409An auth pool cannot email codes: the app has no sender name recipients may see. Set one on the Mail page.
payments_disabled409The payment account is disabled; re-enable it on the project.
deletion_blocked409A delete is refused. blockers[] has one sentence per reason (earnings, a withdrawal, an open checkout, a running deploy, bought domains).
deployment_serving409The deployment cannot be deleted on its own: the project serves it, or it still answers a custom domain. Deploy a new version (or connect the domain) first, or delete the project.
deploy_in_progress409Another deploy, rollback or delete of this function is running. Try again when it finishes.
not_a_static_site409A site password or the app-link files were asked for on a project that is not a static site: a server app or an edge program serves its own paths.
confirm_price / price_moved409A domain renewal above $100 a year needs expectedPriceCents at or above its price (confirm_price), or the price is now above the one you sent (price_moved). price_cents is the price; nothing was charged.
over_cap403A domain renewal above $100 a year asked for by anyone but the workspace owner in the dashboard: the API, an AI connector or another role. dashboard_url is the Domains page where the owner renews it; nothing was charged.
reserved_name400A project name whose address would start with hk- or harakumo, which Harakumo keeps for its own services. Choose another name.
test_mode_off501A test checkout was asked for, and payments test mode is not on yet.
upload_expired410A resumable upload more than six days old. Start a new one.
confirm_forfeit_required409Deleting a workspace would give up a positive Balance; send confirmForfeit: true to accept.
size_required400An upload link asked for maxBytes without size, the file's exact length: a link can only be locked to one exact length, so maxBytes alone would limit nothing. Send both. max_bytes is the limit that applies; no link was made.
file_too_large413An upload link's size is over maxBytes or the plan's per-file cap (1 GB on Hobby, 5 GB on the other plans). max_bytes is the limit that refused it; no link was made.
suppressed422The recipient is on the suppression list.
phone_app422A deploy of an Expo, React Native, Flutter, iOS or Android app with nothing else to deploy (no website, no web build) and no start script that runs a server (only a phone development server such as expo start, or none). The app ships through the App Store and Google Play instead. Nothing was built. app names the kind; docs links Use Harakumo from a phone app; the sentence says how to deploy the app's companion website.
flutter_web_build422A deploy whose build script runs flutter build web. The build machines have no Flutter toolchain, so it was not started and nothing was built. Run flutter build web on your machine and upload the build/web folder, which deploys as a static site.
budget_exhausted422The model used its whole max_tokens budget without an answer. Those tokens are charged; raise max_tokens.
ai_request_refused400 / 413The model refused the request itself (a parameter, or its size). It was not sent to other models; nothing was charged.
rate_limited429A rate limit. retry_after; API quotas add limit and window_seconds.
ai_rate_limited429The AI per-minute limit. limit, window_seconds, retry_after_seconds.
daily_cap429The workspace's own daily email cap.
code_limit429An auth pool's codes asked for from the browser reached the pool's limit or the workspace's share of the email quota. Retry-After.
zone_limit / account_zone_limit429New DNS setups for today are used up: 10 per workspace, or 20 across the workspaces one person owns (that one carries retry_after).
delivery_failed502The mail provider refused the message; message has the reason.
provider_error502A sending domain could not be set up; nothing was saved. Try again in a minute.
ai_unavailable / ai_provider_error502No model could answer right now; nothing was charged. harakumo-1 fails over between vendors.
zone_capacity503Harakumo is not accepting new domain setups for a while; nothing was set up. Buying a domain is not affected.
teardown_incomplete502A delete left some resources behind; the project is kept as teardown_failed. Delete again to retry.
processor_errorvariesThe payment processor refused; the sentence says why.

Handling errors in code

SDK
import Harakumo, { HarakumoError } from '@harakumo/sdk';

try {
  await hk.databases.query(dbId, 'SELECT 1');
} catch (err) {
  if (err instanceof HarakumoError) {
    if (err.status === 429) { /* wait err.body.retry_after seconds, then retry */ }
    if (err.body?.code === 'plan_limit') { /* show err.message to the person */ }
  }
  throw err;
}