Skip to content
GitmoapdocsBeta
Concept· Why does it work like that?Draft

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

json
{
"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": "…" }
]
}
}
FieldPresentWhat it is
codealwaysa coarse, stable category that goes with the status
reasonwhen a specific cause appliesthe specific, stable code for what went wrong
messagealwaysan explanation for a person. It starts with the reason when there is one, but its wording changes — read reason, not the message
violationswhen one request was refused for several reasonsevery refusal, each with its own code and message, and field or rule where they apply
required_scopeon INSUFFICIENT_SCOPEthe 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:

Statuscode
400BAD_REQUEST
401UNAUTHORIZED
403FORBIDDEN, or INSUFFICIENT_SCOPE when the token lacks the operation's scope
404NOT_FOUND
409CONFLICT
413PAYLOAD_TOO_LARGE
422UNPROCESSABLE_ENTITY
429TOO_MANY_REQUESTS
500INTERNAL_ERROR
501NOT_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

StatusRetry?
429 from a quotano — make room or raise the limit first
429 from an egress budgetyes, once the window turns over
503yes, after Retry-After if it is sent, otherwise with growing back-off
500, 502, a timeout or a dropped connectiona 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
409not unchanged — re-read the resource, reconcile, then send a new request
401sign in again, or send a valid token, then retry
400, 413, 422no — fix the request
403, 404no — the request needs a scope, access or a resource it does not have