Errors
Overview
Every failure the API returns comes in one envelope: an HTTP status, a stable code, and a message written for people. Branch on reason when the error carries one and on code when it does not — never on the wording of message.
The API reference lists, for every status of every operation, the codes it can return, and a catalogue explains each code and what to do about it.
How it works
The envelope
{ "error": { "code": "FORBIDDEN", "reason": "BRANCH_PROTECTED", "message": "…", "violations": [ { "code": "BRANCH_PROTECTED", "field": "branch", "message": "…" }, { "code": "PUSH_RULE_VIOLATION", "rule": "require_issue_reference", "field": "message", "message": "…" } ] }}| Field | Present | What it is |
|---|---|---|
code | always | a coarse, stable category that goes with the status |
reason | when a specific cause applies | the specific, stable code for what went wrong |
message | always | an explanation for a person. It starts with the reason when there is one, but its wording changes — read reason, not the message |
violations | when one request was refused for several reasons | every refusal, each with its own code and message, and field or rule where they apply |
required_scope | on INSUFFICIENT_SCOPE | the scope the token lacked |
code, then reason
code tells you what kind of failure it is, and it goes with the status. The common ones:
| Status | code |
|---|---|
400 | BAD_REQUEST |
401 | UNAUTHORIZED |
403 | FORBIDDEN, or INSUFFICIENT_SCOPE when the token lacks the operation's scope |
404 | NOT_FOUND |
409 | CONFLICT |
413 | PAYLOAD_TOO_LARGE |
422 | UNPROCESSABLE_ENTITY |
429 | TOO_MANY_REQUESTS |
500 | INTERNAL_ERROR |
501 | NOT_IMPLEMENTED — a feature this deployment has not configured |
A few codes are more specific than their status: REPOSITORY_EMPTY (409, nothing has been pushed yet), PAYMENT_REQUIRED (402), and the validation codes that carry violations. The catalogue in the API reference lists every code with its status and meaning.
reason tells you which of the many causes behind a status it was — PERMISSION_REQUIRED (a permission you do not hold, named in the message), BRANCH_PROTECTED, HEAD_MOVED, CRITERION_IS_BOUND, LAST_ORGANIZATION_OWNER. Reasons are as stable as codes. Match on reason when it is present, fall back to code when it is not, and show message to the person.
Several refusals at once
Some writes check everything before answering — a commit can meet a branch rule and a push rule in the same request. Then violations lists every refusal, so you can fix them in one round trip, each naming the part of the request it is about: branch, message, or actions[2] for the third file change. The status and reason belong to the refusal that comes first, in order of what you can do about it: who may write comes first, then what is missing, then what moved under you, then what the request itself says.
A 404 can mean private
A private repository or group you are not a member of answers 404 NOT_FOUND, never 403. "Does not exist" and "exists, but not for you" get the same answer, so nobody can find out which private names exist by asking. If you are sure something exists, check that you are a member — and, with a token, that its binding includes it.
Two kinds of 429
429 TOO_MANY_REQUESTS comes from two different places, and the message says which:
- A quota — a count cap on what you create, such as repositories per group. Retrying changes nothing until you delete something, change plan or have the limit raised. See Quotas.
- An egress budget — the bytes you or your organization may pull per hour or per day. It recovers on its own when the window turns over. See Egress.
When any response carries a Retry-After header, wait at least that many seconds before trying again.
Which failures to retry
| Status | Retry? |
|---|---|
429 from a quota | no — make room or raise the limit first |
429 from an egress budget | yes, once the window turns over |
503 | yes, after Retry-After if it is sent, otherwise with growing back-off |
500, 502, a timeout or a dropped connection | a read, yes, with back-off. A write may have taken effect before the failure, so read the resource first and repeat only what did not happen |
409 | not unchanged — re-read the resource, reconcile, then send a new request |
401 | sign in again, or send a valid token, then retry |
400, 413, 422 | no — fix the request |
403, 404 | no — the request needs a scope, access or a resource it does not have |
Related
- Token scopes
INSUFFICIENT_SCOPE, and the binding that can turn a request into403 - Listing isolationwhy a private resource answers
404 - Dry runsseeing every refusal before you make the change
- Edit files in the browserevery reason a commit can be refused
- Repository rulesthe branch and push rules behind
BRANCH_PROTECTEDandPUSH_RULE_VIOLATION