Skip to content

Error Handling

C.O.S. API v1


Error format

C.O.S. uses two error formats depending on the error type:

Standard JSON error (all errors except 429)

Content type: application/json

{
  "error": "NOT_FOUND",
  "message": "Organisation not found",
  "details": []
}
Field Type Description
error string Machine-readable error code (see table below)
message string Human-readable explanation
details array Validation details for VALIDATION_ERROR; empty otherwise

For VALIDATION_ERROR, details contains one object per field:

{
  "error": "VALIDATION_ERROR",
  "message": "Request body validation failed",
  "details": [
    { "field": "name", "issue": "field required" },
    { "field": "industry_section", "issue": "extra fields not permitted" }
  ]
}

RFC 7807 Problem Details (429 only)

Content type: application/problem+json

{
  "type": "https://cosprotocol.io/errors/rate-limit-exceeded",
  "title": "Rate Limit Exceeded",
  "status": 429,
  "detail": "Organisation rate limit exceeded. Retry after 1 second.",
  "cos_error_code": "RATE_LIMIT_EXCEEDED",
  "retry_after": 1,
  "instance": "/v1/organisations/7c9e6679-7425-40de-944b-e07fc1f90ae7"
}

Error codes

HTTP status error / cos_error_code Meaning
400 VALIDATION_ERROR Request body or parameters failed validation. Check details array.
401 UNAUTHORIZED Missing, malformed, or revoked API key.
403 FORBIDDEN Valid key, but insufficient permissions (e.g. platform key required).
404 NOT_FOUND The resource does not exist or is not visible to your key.
429 RATE_LIMIT_EXCEEDED Per-org or global rate limit exceeded. See Rate Limits.
500 INTERNAL_ERROR Unexpected server error. Transient; safe to retry with backoff.
503 SERVICE_UNAVAILABLE Dependency unavailable. Transient; retry with backoff.

Retry semantics

Error Retry? Guidance
400 VALIDATION_ERROR No Fix the request payload.
401 UNAUTHORIZED No Check your key; it may be revoked.
403 FORBIDDEN No You need a different key type.
404 NOT_FOUND No Resource doesn't exist.
429 RATE_LIMIT_EXCEEDED Yes Wait Retry-After seconds, then retry.
500 INTERNAL_ERROR Yes Retry with exponential backoff.
503 SERVICE_UNAVAILABLE Yes Retry with exponential backoff.

Checking the error code in code

Python:

import requests

resp = requests.get(
    "https://cosprotocol.io/v1/organisations/bad-uuid",
    headers={"Authorization": f"Bearer {API_KEY}"},
)

if not resp.ok:
    # 429 uses application/problem+json; others use application/json
    body = resp.json()
    error_code = body.get("cos_error_code") or body.get("error")
    print(f"Error {resp.status_code}: {error_code}{body.get('detail') or body.get('message')}")

curl — inspect status code only:

STATUS=$(curl -s -o /dev/null -w "%{http_code}" \
  -H "Authorization: Bearer $COS_API_KEY" \
  https://cosprotocol.io/v1/organisations/bad-uuid)
echo "HTTP $STATUS"

Common mistakes

400 with industry_section in detailsindustry_section is read-only and is set by C.O.S. internally. Remove it from the request body.

400 with slug in detailsslug is immutable after initial assignment. It cannot be changed via PUT or PATCH.

403 instead of 404 — If you request a resource belonging to another organisation, the API returns 403 FORBIDDEN rather than 404 NOT_FOUND. This prevents enumeration.

401 on a key you just issued — Check that the full key string (including the cos_live_ prefix) is being sent and that there are no extra spaces or newlines in the header value.