Errors
Every failure has the same shape, and every status code means one thing. Most of the value on this page is in a single distinction: a 401 about you and a 401 about a connector require completely different fixes.
The response shape
{
"error": "Connect Meta first (Settings ▸ Connectors ▸ Meta).",
"connector": "meta",
"meta": { "userOutOfCredits": true }
}
| Field | Present | Meaning |
|---|---|---|
error | Always | A human-readable string, safe to show a user. Vendor names, credentials and raw upstream response bodies are scrubbed out of it. |
connector | Only on a connector problem | The provider key. Its presence is load-bearing — see below. |
meta | Sometimes | A small allowlisted set of machine-readable hints: userOutOfCredits, suggestModel, contentStop, policyBlock, status. Never the raw upstream message. |
Successful responses are conventionally enveloped as { "data": … }.
The one rule worth memorising
A 401 without a connector field means your session or key is bad. A 401 with one means that provider is not connected.
Branch on the field's presence, not on the message text. Getting this backwards sends a user to re-authenticate when their login was fine, or to reconnect Meta when their key was revoked — and both look like the product is broken.
The marker is applied in three layers so it cannot be missed: the helper that mints these errors always sets it, the route wrapper copies it onto the body, and a belt stamps it onto any 401 from a request that had already resolved a connector context. That last one covers the errors we do not mint ourselves — a refresh token the provider refuses, or a provider's own 401 echoed back.
Status codes
| Code | Means | Do |
|---|---|---|
400 | Bad parameter, or the right call on the wrong surface. | Read error; it names the parameter. |
401 bare | Your key is invalid, revoked, or absent. | Re-authenticate. A malformed hmk_ token is a hard 401, never a silent downgrade. |
401 + connector | That provider is not connected for this workspace. | Send the user to link it. An agent cannot — OAuth needs a browser. |
402 | Out of credits, or the balance cannot cover the estimate. | Check meta.userOutOfCredits. Offer buy_credits. |
403 | Authenticated, but not permitted. Not a member of that workspace; or a key attempting a browser-only action. | Do not retry. The message says which. |
404 | No such object — or an object you may not see. The two are deliberately indistinguishable so an id cannot be used as an existence oracle. | Re-resolve the id with a list_ tool. |
409 | A publish conflict: an identical publish is in flight, or an earlier one's outcome is unknown. | See idempotency. Do not blindly retry. |
413 | Payload too large. | Shrink it. The limit is in the message. |
422 | Content policy, or an unprocessable render. | Check meta.policyBlock / meta.contentStop. Change the prompt, do not retry it unchanged. |
429 | Rate limited — by us, or by the platform. | Back off. If it came from a platform, the message says the connection is fine and roughly how long to wait. |
501 | The capability is not configured on this server. | Not retryable. For a connector, it means credentials are absent or a documented approval gate is closed. |
502 | A provider refused us. Their 401/403 is mapped here deliberately — insufficient scope is not "you are logged out". | Retry once, then surface it. |
503 | A provider is at capacity, or a read failed and we will not guess. | Retry with backoff. |
A throttle is not a broken connection
When a platform rate-limits us, the response is 429 and it says explicitly that the connection is fine, that this is temporary, and that re-authorising will not clear it — because re-authorising cannot clear a rate limit, and the obvious fix is the one that provably does not work.
The same applies to a wrong id: a platform whose own not-found message contains the word “permissions” still gets classified as 404, naming the id, never as “reconnect”.
A failed read is not an empty result
This one shapes several responses and is worth stating because it is the opposite of what a naive implementation does. When we cannot read something, we say so — we do not return an empty list.
- A brand's shared connectors that fail to load answer
503, not an empty roster. “Nothing is connected” and “I could not tell you what is connected” are different answers, and the first one sends someone to reconnect a healthy account. - A delete whose read-back could not run reports “could not confirm”, never “done”.
Treat an empty array as real data and a 503 as unknown, and your retry logic will be right.
Reporting a problem
From inside your agent, without leaving the session: report_bug files a report against your account, and request_feature asks for a capability we do not have. Both are free.