gRPC status codes explained
Every gRPC call completes with a status consisting of a numeric code and an optional message. Unlike HTTP, gRPC defines a fixed, language-neutral set of 17 codes (0–16) that every implementation shares. This reference lists all of them with their numeric value, the conventional HTTP status they map to in gateways, a plain-language meaning, and whether the call is generally safe to retry.
How it works
gRPC status codes are defined in the grpc-status trailer of every response. Code 0 (OK) means success; any non-zero code is an error. The codes split roughly into three groups:
- Client errors the caller should fix:
INVALID_ARGUMENT(3),NOT_FOUND(5),PERMISSION_DENIED(7),FAILED_PRECONDITION(9),UNAUTHENTICATED(16). - Transient errors worth retrying:
DEADLINE_EXCEEDED(4),RESOURCE_EXHAUSTED(8),ABORTED(10),UNAVAILABLE(14). - Server faults that signal bugs:
UNKNOWN(2),INTERNAL(13),DATA_LOSS(15).
When a gRPC service is exposed over HTTP via a gateway, codes map to HTTP statuses by convention — NOT_FOUND becomes 404, UNAVAILABLE becomes 503, and so on. That mapping is shown per row so you can reason about both protocols at once.
Quick reference: all 17 codes
| Code | Name | HTTP equivalent | Retry? |
|---|---|---|---|
| 0 | OK | 200 | — |
| 1 | CANCELLED | 499 | No |
| 2 | UNKNOWN | 500 | No |
| 3 | INVALID_ARGUMENT | 400 | No |
| 4 | DEADLINE_EXCEEDED | 504 | Maybe (idempotent only) |
| 5 | NOT_FOUND | 404 | No |
| 6 | ALREADY_EXISTS | 409 | No |
| 7 | PERMISSION_DENIED | 403 | No |
| 8 | RESOURCE_EXHAUSTED | 429 | Yes (with backoff) |
| 9 | FAILED_PRECONDITION | 400 | No |
| 10 | ABORTED | 409 | Yes (with backoff) |
| 11 | OUT_OF_RANGE | 400 | No |
| 12 | UNIMPLEMENTED | 501 | No |
| 13 | INTERNAL | 500 | No |
| 14 | UNAVAILABLE | 503 | Yes (with backoff) |
| 15 | DATA_LOSS | 500 | No |
| 16 | UNAUTHENTICATED | 401 | No |
Distinguishing the easiest-to-confuse codes
FAILED_PRECONDITION vs ABORTED vs UNAVAILABLE: These three look similar but mean different things. FAILED_PRECONDITION means the system is not in a state that allows the operation — for example, deleting a non-empty directory. ABORTED means a concurrency conflict such as a test-and-set failing or a transaction aborting; the client should usually retry at a higher level. UNAVAILABLE means the server is temporarily down or overloaded and the call never reached application logic.
UNKNOWN vs INTERNAL: UNKNOWN surfaces when a non-gRPC error leaks through without a defined status code — for example, an unhandled exception in middleware. INTERNAL is reserved for serious invariant violations inside a service, like corrupted data or a failed serialisation. Both map to HTTP 500, but INTERNAL signals a bug in the server; UNKNOWN suggests an integration or wiring problem.
CANCELLED vs DEADLINE_EXCEEDED: CANCELLED means the client cancelled the request. DEADLINE_EXCEEDED means the deadline passed before the server completed, but the server may have finished anyway — the state of the operation is ambiguous, which matters for non-idempotent calls.
Retry guidance and example
A safe retry policy retries only idempotent calls that failed with UNAVAILABLE, and uses exponential backoff:
attempt 1 -> wait 100ms
attempt 2 -> wait 200ms
attempt 3 -> wait 400ms (then give up)
Avoid retrying INVALID_ARGUMENT, NOT_FOUND or PERMISSION_DENIED — the inputs must change first. For RESOURCE_EXHAUSTED, honour any retry-delay the server returns rather than hammering it. Use the table above to confirm a code’s number and HTTP equivalent before wiring up interceptors.