---
title: "Errors"
description: "Understand API error responses and status codes."
section: "reference"
url: "https://forkline.dev/docs/reference/errors"
markdown: "https://forkline.dev/docs/reference/errors.md"
source: "src/content/docs/reference/errors.mdx"
---

# Errors

The Forkline API returns consistent error responses in JSON format.

## Error Envelope

All errors return a JSON object with two fields:

```json
{
  "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:

```json
{
  "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:

```json
{
  "error": "Token lacks required scope: runners:write",
  "code": "FORBIDDEN"
}
```

To resolve this, create a new token with the required scope. See [Authentication](https://forkline.dev/docs/reference/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:

```json
{
  "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:

```json
{
  "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:

```json
{
  "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.

```javascript
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.
