Errors
The Forkline API returns consistent error responses in JSON format.
Error Envelope
All errors return a JSON object with two fields:
{
"error": "Human-readable description of the error",
"code": "ERROR_CODE"
}
error: A human-readable message explaining what went wrong. This message may change over time and should not be used for programmatic error handling.code: A stable machine-readable error code. Use this field for programmatic error handling.
HTTP Status Codes
| Status | Code | Description |
|---|---|---|
| 400 | BAD_REQUEST |
The request is malformed or missing required parameters |
| 401 | UNAUTHORIZED |
Authentication failed: token is missing, expired, or revoked |
| 403 | FORBIDDEN |
The token does not have the required scope for this endpoint |
| 404 | NOT_FOUND |
The requested resource does not exist, or the endpoint is not in the public API allowlist |
| 402 | INSUFFICIENT_CREDITS |
Special case: not enough credits to perform the operation (e.g., starting a runner) |
| 409 | CONFLICT |
The request conflicts with the current state of the resource |
| 429 | TOO_MANY_REQUESTS |
Rate limit exceeded |
| 500 | INTERNAL_ERROR |
An unexpected error occurred on the server |
| 503 | SERVICE_UNAVAILABLE |
The service is temporarily unavailable |
Authentication Errors (401)
A 401 Unauthorized response indicates that the request lacks valid authentication. Common causes:
- Missing
Authorizationheader - Malformed token (incorrect format)
- Expired token
- Revoked token
Example:
{
"error": "Invalid or missing API token",
"code": "UNAUTHORIZED"
}
Note: The
errormessage is human-readable and may change. Use the stablecodefield (UNAUTHORIZED) for programmatic handling.
Authorization Errors (403)
A 403 Forbidden response indicates that the token is valid but does not have the required scope for the requested endpoint.
Example:
{
"error": "Token lacks required scope: runners:write",
"code": "FORBIDDEN"
}
To resolve this, create a new token with the required scope. See Authentication for details on scopes.
Insufficient Credits (402)
A 402 Payment Required response with code INSUFFICIENT_CREDITS is returned when an operation requires credits that your account does not have. This typically occurs when starting runners.
Example:
{
"error": "Insufficient credits to start runner",
"code": "INSUFFICIENT_CREDITS",
"required": 100,
"available": 50
}
The response may include additional fields (e.g., required, available) to help you understand the credit shortfall.
Rate Limiting (429)
A 429 Too Many Requests response indicates that you have exceeded the API rate limit.
Example:
{
"error": "Rate limit exceeded. Try again in 30 seconds",
"code": "TOO_MANY_REQUESTS"
}
The API enforces a default rate limit of 20 requests per second per client IP, with burst allowance. If the response includes a Retry-After header, use it as the recommended wait time (note: some ingress proxies may strip this header).
Not Found (404)
A 404 Not Found response can mean:
- The requested resource does not exist (e.g., repository ID not found)
- The endpoint is not in the public API allowlist
The public API uses a positive allowlist — only explicitly registered endpoints are accessible. All other paths return 404, even if they exist on the internal API.
Conflict (409)
A 409 Conflict response indicates that the request conflicts with the current state of the resource. Common scenarios:
- Attempting to create a resource that already exists
- Attempting to delete a resource that is in use
- Attempting to update a resource that has been modified by another request
Bad Request (400)
A 400 Bad Request response indicates that the request is malformed. Common causes:
- Invalid JSON syntax
- Missing required fields
- Invalid field values (e.g., wrong type, out of range)
- Invalid query parameters
Example:
{
"error": "Invalid request body: 'name' is required",
"code": "BAD_REQUEST"
}
Internal Errors (500, 503)
A 500 Internal Server Error or 503 Service Unavailable response indicates a problem on the server side. These errors are typically transient — retry the request after a short delay.
If the problem persists, check the Forkline status page or contact support.
Error Handling Best Practices
Check the Status Code
Always check the HTTP status code first. The error code and message provide additional context, but the status code is the primary indicator of success or failure.
Use Error Codes for Logic
Use the code field for programmatic error handling. Error messages may change over time, but error codes are stable.
const response = await fetch('https://api.forkline.dev/v1/me', {
headers: { 'Authorization': `Bearer ${token}` }
});
if (!response.ok) {
const error = await response.json();
if (response.status === 401) {
// Token is invalid — prompt user to re-authenticate
redirectToLogin();
} else if (error.code === 'FORBIDDEN') {
// Token lacks required scope
showScopeError(error.error);
} else if (error.code === 'TOO_MANY_REQUESTS') {
// Rate limited — wait and retry
await retryAfterDelay(response.headers.get('Retry-After'));
} else {
// Other error
showError(error.error);
}
}
Retry Transient Errors
Retry 429, 500, and 503 errors with exponential backoff. Do not retry 400, 401, 403, 404, 409, or 402 errors — these indicate client-side problems that will not resolve on retry.
Log Error Details
Log the full error response (status code, code, and message) for debugging. Include the request ID if available (check response headers).
Debugging
Swagger UI
The interactive API documentation at /v1/docs shows the expected request format and possible error responses for each endpoint.
Request ID
Some error responses include a X-Request-Id header. Include this ID when contacting support to help diagnose the issue.
Verbose Logging
Enable verbose logging in your HTTP client to see the full request and response, including headers and body. This helps identify issues with authentication, content types, and request formatting.