View as Markdown

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 Authorization header
  • Malformed token (incorrect format)
  • Expired token
  • Revoked token

Example:

{
  "error": "Invalid or missing API token",
  "code": "UNAUTHORIZED"
}

Note: The error message is human-readable and may change. Use the stable code field (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:

  1. The requested resource does not exist (e.g., repository ID not found)
  2. 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.