API Errors
BitVault uses a stable, machine-readable error taxonomy defined in pkg/apperr.
Error codes are versioned with the API — a code that ships in v1 will never be
silently repurposed.
Error Response Shape
Every error response is a JSON object with three fields:
{
"code": "NOT_FOUND",
"message": "file node abc123 does not exist",
"request_id": "01J2K4M6N8P0Q2R4S6T8V0W2X4"
}
| Field | Type | Description |
|---|---|---|
code |
string |
Machine-readable error code from pkg/apperr |
message |
string |
Human-readable description; may include safe context details |
request_id |
string |
Unique ID for this request; maps to the distributed trace |
The message field is informational and may change between releases. Build
integrations against code, not message.
HTTP Status Mapping
| HTTP Status | Semantics | Example code values |
|---|---|---|
400 Bad Request |
Validation failure or malformed input | INVALID_ARGUMENT, MISSING_REQUIRED_FIELD |
401 Unauthorized |
Not authenticated; token missing, expired, or invalid | UNAUTHENTICATED, TOKEN_EXPIRED |
403 Forbidden |
Authenticated but not authorized for this resource | PERMISSION_DENIED, INSUFFICIENT_SCOPE |
404 Not Found |
Resource does not exist (or hidden for authz) | NOT_FOUND |
409 Conflict |
Resource already exists or state conflict | ALREADY_EXISTS, VERSION_CONFLICT |
413 Payload Too Large |
Quota exceeded or file too large for tier | QUOTA_EXCEEDED, FILE_TOO_LARGE |
429 Too Many Requests |
Rate limit exceeded | RATE_LIMITED |
500 Internal Server Error |
Unexpected server error; investigate via request_id |
INTERNAL |
503 Service Unavailable |
Dependency unavailable; safe to retry with backoff | UNAVAILABLE |
Example Error Responses
404 Not Found:
{
"code": "NOT_FOUND",
"message": "node 01J2K4M6N8P0Q2R4 does not exist or you do not have access",
"request_id": "01J2K4M6N8P0Q2R4S6T8V0W2X4"
}
409 Version Conflict (sync push with stale base):
{
"code": "VERSION_CONFLICT",
"message": "base version 7 is not current (current: 9); a conflicted copy has been created",
"request_id": "01J2K4M6N8P0Q2R4S6T8V0W2X5"
}
429 Rate Limited:
{
"code": "RATE_LIMITED",
"message": "token rate limit exceeded; retry after 2026-06-12T14:30:00Z",
"request_id": "01J2K4M6N8P0Q2R4S6T8V0W2X6"
}
Retrying Errors
| Code | Retry? | Strategy |
|---|---|---|
INTERNAL |
Maybe | Retry once with backoff; if persists, report request_id |
UNAVAILABLE |
Yes | Exponential backoff with jitter |
RATE_LIMITED |
Yes | Wait until Retry-After header value |
UNAUTHENTICATED |
Refresh token first | Obtain new access token, then retry |
INVALID_ARGUMENT |
No | Fix the request |
PERMISSION_DENIED |
No | User lacks the required permission |
VERSION_CONFLICT |
Depends | For sync: handle the conflicted copy; for others: re-fetch and retry |
:::tip request_id
Include the request_id from any error response when filing a support request
or bug report. It maps directly to the distributed trace for that request in
Jaeger/Tempo, allowing the exact execution path to be inspected end-to-end.
:::