Error Codes
All club API errors follow a single JSON envelope. This page documents the shape, the codes that appear in it, and when each one occurs.
Error Envelope
Every error response contains an error object with a code and a human-readable message:
{ "error": { "code": "NotFound", "message": "Package 'nonexistent' was not found." }}| Field | Type | Description |
|---|---|---|
error.code | string | Machine-readable error code (PascalCase) |
error.message | string | Human-readable description |
The code field is stable and safe for programmatic error handling. Messages may change between versions — use them for display only.
Common Error Codes
| Code | HTTP Status | When |
|---|---|---|
MissingAuthentication | 401 | No credentials provided, or bad email/password, or invalid/expired/revoked token |
InsufficientPermissions | 403 | Authenticated but lacking the required role or scope |
CsrfMismatch | 403 | Cookie-authenticated state-changing request with a missing or invalid CSRF token |
NotFound | 404 | Resource does not exist |
InvalidInput | 400 | Malformed input or validation failure |
PackageRejected | 400 | Package upload failed validation (bad archive, name, version, or size) |
Conflict | 409 | Resource already exists, or conflicting state (e.g. duplicate version with different content) |
RateLimitExceeded | 429 | Too many requests from this IP |
VerificationTemporarilyUnavailable | 503 | DNS verification could not complete due to a transient failure |
InternalError | 500 | Unexpected server error |
Examples
MissingAuthentication (401)
Sent when an endpoint requires authentication but none was provided. The
same code is also returned by POST /api/auth/login for a bad email or
password, and by any endpoint when the Bearer token is invalid, expired,
or revoked (only the message differs).
{ "error": { "code": "MissingAuthentication", "message": "Authentication required." }}{ "error": { "code": "MissingAuthentication", "message": "Invalid email or password." }}Response headers on a 401 include:
HTTP/1.1 401 UnauthorizedWWW-Authenticate: Bearer realm="pub", message="Authentication required."InsufficientPermissions (403)
Returned when the caller is authenticated but the operation requires a higher role, scope, or relationship to the resource.
{ "error": { "code": "InsufficientPermissions", "message": "Admin privileges required." }}Common causes: token lacks the write or admin scope; user is not an uploader of the package; user is not a publisher admin; only the server owner can transfer ownership.
NotFound (404)
{ "error": { "code": "NotFound", "message": "Package 'nonexistent' was not found." }}InvalidInput (400)
Used for malformed input and validation errors across the API.
{ "error": { "code": "InvalidInput", "message": "Cannot remove the last uploader." }}PackageRejected (400)
Returned when a package upload fails validation during the finalize step.
{ "error": { "code": "PackageRejected", "message": "'My-Package' is not a valid package name. Package names must be lowercase, start with a letter, and contain only letters, numbers, and underscores." }}Conflict (409)
{ "error": { "code": "Conflict", "message": "A user with that email already exists." }}{ "error": { "code": "Conflict", "message": "Version is already retracted." }}RateLimitExceeded (429)
Returned from rate-limited endpoints (/api/auth/login, /api/auth/signup, /api/setup/verify, /api/setup/complete, /api/invites/*). See the overview for per-endpoint limits.
{ "error": { "code": "RateLimitExceeded", "message": "Too many requests. Try again later." }}InternalError (500)
Unexpected server error. Stack traces and internal details are never returned; check the server logs for diagnostics.
{ "error": { "code": "InternalError", "message": "An unexpected error occurred." }}HTTP Status Summary
| Status | Meaning | Typical cause |
|---|---|---|
| 200 | OK | Successful GET, PUT, DELETE, idempotent POST |
| 201 | Created | Successful resource-creating POST |
| 202 | Accepted | Async acceptance (e.g. signup) |
| 302 | Found | Upload-flow redirect; public archive served via external storage |
| 303 | See Other | Legacy archive URL redirect |
| 400 | Bad Request | InvalidInput, PackageRejected |
| 401 | Unauthorized | MissingAuthentication |
| 403 | Forbidden | InsufficientPermissions, CsrfMismatch |
| 404 | Not Found | NotFound |
| 409 | Conflict | Conflict |
| 429 | Too Many Requests | RateLimitExceeded |
| 500 | Internal Server Error | InternalError |
| 503 | Service Unavailable | VerificationTemporarilyUnavailable, or health check reports degraded status |