Skip to content

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."
}
}
FieldTypeDescription
error.codestringMachine-readable error code (PascalCase)
error.messagestringHuman-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

CodeHTTP StatusWhen
MissingAuthentication401No credentials provided
InvalidCredentials401Bad email/password, or invalid/expired/revoked token
Forbidden403Authenticated but lacking the required role or scope
NotFound404Resource does not exist
BadRequest400Malformed input, validation failure, or rejected package
Conflict409Resource already exists, or conflicting state (e.g. duplicate version with different content)
RateLimited429Too many requests from this IP
InternalError500Unexpected server error

Examples

MissingAuthentication (401)

Sent when an endpoint requires authentication but none was provided.

{
"error": {
"code": "MissingAuthentication",
"message": "Authentication required. Provide a valid Bearer token."
}
}

Response headers include:

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer realm="pub", message="Authentication required."

InvalidCredentials (401)

Returned by POST /api/auth/login for bad passwords, and by any endpoint when the Bearer token is invalid, expired, or revoked.

{
"error": {
"code": "InvalidCredentials",
"message": "Invalid email or password."
}
}

Forbidden (403)

Returned when the caller is authenticated but the operation requires a higher role, scope, or relationship to the resource.

{
"error": {
"code": "Forbidden",
"message": "Admin access 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."
}
}

BadRequest (400)

Used for validation errors across the entire API, including publish validation.

{
"error": {
"code": "BadRequest",
"message": "Invalid package name 'My-Package'. Package names must be lowercase alphanumeric with underscores, 1-64 characters."
}
}
{
"error": {
"code": "BadRequest",
"message": "Cannot remove the last uploader of a package."
}
}

Conflict (409)

{
"error": {
"code": "Conflict",
"message": "A user with email 'jane@example.com' already exists."
}
}
{
"error": {
"code": "Conflict",
"message": "Version 2.1.0 already exists with a different archive hash."
}
}

RateLimited (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": "RateLimited",
"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. Please try again later."
}
}

HTTP Status Summary

StatusMeaningTypical cause
200OKSuccessful GET, PUT, DELETE, idempotent POST
201CreatedSuccessful resource-creating POST
202AcceptedAsync acceptance (e.g. signup)
302FoundUpload-flow redirect; public archive served via external storage
303See OtherLegacy archive URL redirect
400Bad RequestBadRequest
401UnauthorizedMissingAuthentication or InvalidCredentials
403ForbiddenForbidden
404Not FoundNotFound
409ConflictConflict
429Too Many RequestsRateLimited
500Internal Server ErrorInternalError
503Service UnavailableHealth check reports degraded status