Error Handling
The JJHub API uses a consistent, Gitea-compatible error format across all endpoints. Every error response is a JSON object with a human-readablemessage and an optional errors array containing field-level details.
Error Response Structure
All API errors follow this shape:| Field | Type | Description |
|---|---|---|
message | string | A human-readable summary of the error. Always present. |
errors | array | An optional list of field-level errors. Present when the server can pinpoint the problem to specific fields on a resource. |
errors[].resource | string | The API resource type (e.g., Issue, Repository, LandingRequest). |
errors[].field | string | The field on the resource that caused the error. |
errors[].code | string | A machine-readable error code. See Field Error Codes below. |
errors array is omitted from the response body.
HTTP Status Codes
| Status | Meaning | When It Happens |
|---|---|---|
400 | Bad Request | Malformed JSON, missing required query parameters, or otherwise unparseable input. |
401 | Unauthorized | Missing, expired, or invalid authentication credentials. |
403 | Forbidden | Valid credentials but insufficient permissions for the requested action. |
404 | Not Found | The requested resource does not exist, or the authenticated user does not have access to it. |
409 | Conflict | The request conflicts with current server state (e.g., duplicate repository name, email already in use). |
413 | Payload Too Large | The request body exceeds the maximum allowed size (1 MB for most endpoints). |
415 | Unsupported Media Type | The Content-Type header is not application/json. |
422 | Unprocessable Entity | The request body is valid JSON but fails validation (e.g., missing required fields, invalid values). |
429 | Too Many Requests | Rate limit exceeded. See the X-RateLimit-* response headers for details. |
500 | Internal Server Error | An unexpected server-side error occurred. If this persists, contact support. |
504 | Gateway Timeout | The request took longer than the 30-second server timeout. |
Field Error Codes
When theerrors array is present, each entry contains a code field with one of the following values:
| Code | Meaning |
|---|---|
missing | The resource referenced by the request does not exist. |
missing_field | A required field was not provided in the request body. |
invalid | The field value is present but not valid (wrong type, out of range, bad format). |
already_exists | The field value must be unique and a resource with this value already exists. |
unprocessable | The field value is well-formed but cannot be processed in the current context. |
Example Error Responses
Validation Error (422)
A request to create an issue without a title:Not Found (404)
A request for a repository that does not exist:Authentication Error (401)
A request with an invalid or expired token:Permission Error (403)
A request to delete a repository the user does not own:Conflict (409)
A request to create a repository with a name that already exists:Multiple Field Errors (422)
A request with several invalid fields:Rate Limiting (429)
When you exceed the rate limit, the response includes headers indicating when you can retry:X-RateLimit-Reset value is a Unix timestamp indicating when the rate limit window resets. Authenticated requests are limited to 5,000 per hour. Unauthenticated requests are limited to 60 per hour. Search endpoints have a separate limit of 30 requests per minute.
Best Practices
Check the status code first
Use the HTTP status code to determine the category of error before parsing the body. This lets you handle broad error classes (auth failures, not found, server errors) without parsing JSON.Use the errors array for user-facing messages
When the errors array is present, use it to display specific field-level feedback. The resource, field, and code triple gives you enough information to map errors to form fields or CLI flags.
Handle rate limits gracefully
Read theX-RateLimit-Remaining header on every response. When it reaches zero, wait until the X-RateLimit-Reset timestamp before sending more requests. For automated clients, implement exponential backoff on 429 responses.
Do not rely on error message text
Themessage field is intended for human readers and may change without notice. Build your error-handling logic around status codes and errors[].code values, which are part of the stable API contract.
Retry only on appropriate errors
Only retry on429 (after respecting the rate limit reset time) and 500/504 (with exponential backoff). Do not retry 400, 401, 403, 404, 409, or 422 responses, as these indicate client-side problems that will not resolve by retrying.