Skip to content

Errors

SkyState uses standard HTTP status codes and a consistent JSON error body for all error responses.


Error Response Format

All error responses use this shape:

json
{
  "error": "not_found",
  "message": "Project not found"
}
FieldTypeDescription
errorstringMachine-readable error code (snake_case)
messagestringHuman-readable explanation

Note: The 402 (over limit) and 409 (version conflict) responses use different shapes. See the sections below.


HTTP Status Codes

CodeMeaningWhen it occurs
200OKSuccessful read or action
201CreatedResource created (config versions, projects)
204No ContentSuccessful update or delete with no body
302RedirectOAuth flow redirects
400Bad RequestValidation error, missing required fields, invalid format
401UnauthorizedMissing or invalid authentication credentials
402Payment RequiredResource or storage limit exceeded for the account's tier
403ForbiddenAuthenticated but not permitted (e.g. dev key used outside development)
404Not FoundResource does not exist or does not belong to the account
409ConflictVersion conflict on PATCH, or slug already taken
429Too Many RequestsMonthly API request quota exceeded
500Internal Server ErrorUnexpected server-side error
503Service UnavailableHealth check indicates database is unreachable

Common Error Codes

400 Bad Request

errormessage (example)Endpoint
invalid_slug"Slugs must contain only lowercase alphanumeric characters and hyphens"Account slug update, public config
invalid_slug_format"Slugs must contain only lowercase alphanumeric characters and hyphens"Public config
invalid_config"..."Config create (PUT)
invalid_patch"..."Config PATCH
if_match_required"If-Match header with version string is required for PATCH requests"Config PATCH
if_match_required"If-Match: * is not supported. Provide the explicit version from your last read."Config PATCH
validation_error"..."Project create, billing checkout, tier change
checkout_error"Account not found"Billing checkout
portal_error"..."Billing portal

402 Payment Required — Over Limit

When a resource or storage limit is exceeded, the server returns a structured LimitResponse instead of the standard error shape:

json
{
  "resource": "projects",
  "current": 3,
  "limit": 3,
  "tier": "free",
  "upgradeTier": "hobby",
  "checkoutUrl": "/upgrade",
  "code": "LIMIT_PROJECTS"
}
FieldTypeDescription
resourcestringThe resource that is over limit (e.g. "projects")
currentintegerCurrent count
limitintegerMaximum allowed on the current tier
tierstringAccount's current tier
upgradeTierstring or nullNext tier that would lift the limit; null if already on highest tier
checkoutUrlstring or nullFrontend URL path for upgrading
codestringMachine-readable limit code (e.g. "LIMIT_PROJECTS")

403 Forbidden

errormessageEndpoint
forbidden"Dev API key required."Dev config PATCH
forbidden"Dev API keys can only write to the development environment."Dev config PATCH

404 Not Found

errormessageEndpoint
not_found"Project not found"Project endpoints
not_found"Account not found"Account endpoints
not_found"Dev API key not found"Dev key revoke

409 Conflict — Version Conflict (Config PATCH)

When a PATCH request's If-Match version does not match the current version, the server returns a ConflictResponse:

json
{
  "code": "VERSION_CONFLICT",
  "message": "The config has been modified since your last read.",
  "currentVersion": "1.2.5"
}
FieldTypeDescription
codestring"VERSION_CONFLICT"
messagestringHuman-readable explanation
currentVersionstring or nullThe actual current version string

The response also includes an ETag header with the current version.

To resolve a conflict: fetch the latest config, re-apply your changes, and retry the PATCH with the updated If-Match value.

409 Conflict — Slug Already Taken

When setting an account slug that is already in use:

json
{
  "error": "slug_conflict",
  "message": "The slug 'jane-smith' is already taken."
}

429 Too Many Requests — Monthly Quota

See Rate Limiting for the full 429 response format.

500 Internal Server Error

errormessage
internal_error"An unexpected error occurred."

503 Service Unavailable — Health Check

json
{
  "status": "degraded",
  "database": "unreachable"
}

Authentication Errors

Authentication failures return 401 Unauthorized with no JSON body. This occurs when:

  • The Authorization header is missing on a protected endpoint
  • The Bearer token is invalid or rejected by GitHub
  • The Dev API key does not exist or has been revoked
  • The Dev API key hash does not match any stored key

Handling Errors in Client Code

A general-purpose error handler should:

  1. Check the HTTP status code first.
  2. For 400, 403, 404, 500: parse the { error, message } body.
  3. For 402: parse the LimitResponse body and surface the upgrade prompt.
  4. For 409 on PATCH: parse ConflictResponse, use currentVersion to refetch and retry.
  5. For 429: read Retry-After and wait before retrying.
  6. For 401: prompt the user to re-authenticate.

Built with VitePress