> ## Documentation Index
> Fetch the complete documentation index at: https://docs.simplehost.dev/llms.txt
> Use this file to discover all available pages before exploring further.

# Errors

> Error codes and response format

## Error Response Format

All API errors return JSON with a consistent structure. Error messages are written in a conversational tone -- they tell you what went wrong and often include a `hint` field with a suggestion for how to fix it.

```json theme={null}
{
  "error": "Custom handles are a Hobby plan feature. Upgrade to Hobby ($5/mo) to claim yourname.simplehost.dev.",
  "code": "PLAN_LIMIT",
  "status": 403,
  "hint": "Call POST /api/billing/checkout with your API key to get an upgrade link."
}
```

Some errors also include contextual fields like `upgrade` with actionable next steps (e.g., a checkout endpoint to call).
Some publishing errors also include structured remediation details so an agent can recover safely.

## Error Codes

| Code                  | HTTP Status | Description                                                                            |
| --------------------- | ----------- | -------------------------------------------------------------------------------------- |
| `INVALID_REQUEST`     | 400         | Request validation failed — missing or invalid fields                                  |
| `INVALID_PATH`        | 400         | File path validation failed (contains `..`, starts with `/`, etc.)                     |
| `INVALID_HASH`        | 400         | Hash is not a valid 64-character lowercase hex string                                  |
| `UNAUTHORIZED`        | 401         | Missing, invalid, or expired API key                                                   |
| `NOT_FOUND`           | 404         | Resource not found or not owned by the authenticated user                              |
| `RATE_LIMITED`        | 429         | Rate limit exceeded — check `X-RateLimit-Reset` header                                 |
| `FILE_TOO_LARGE`      | 413         | Individual file exceeds plan limit (200 MB free, 5 GB hobby)                           |
| `STORAGE_EXCEEDED`    | 413         | Total storage quota exceeded (200 MB free, 100 GB hobby)                               |
| `SITE_LIMIT_EXCEEDED` | 403         | Maximum site count reached (500 for free plan)                                         |
| `PLAN_LIMIT`          | 403         | Feature not available on current plan                                                  |
| `SECRET_DETECTED`     | 400         | Finalize found exposed private keys or secrets in files that would be publicly visible |
| `INTERNAL_ERROR`      | 500         | Unexpected server error                                                                |

## Common Scenarios

### Invalid API Key

```json theme={null}
{
  "error": "Invalid API key",
  "code": "UNAUTHORIZED",
  "status": 401
}
```

**Fix:** Check that your API key starts with `sh_live_` and is complete (no truncation).

### Rate Limited

```json theme={null}
{
  "error": "Rate limit exceeded. Try again later.",
  "code": "RATE_LIMITED",
  "status": 429
}
```

**Fix:** Wait until the time indicated by the `X-RateLimit-Reset` header. See [Rate Limits](/api-reference/rate-limits).

### Storage Exceeded

```json theme={null}
{
  "error": "Storage quota exceeded. Current usage: 195 MB of 200 MB.",
  "code": "STORAGE_EXCEEDED",
  "status": 413
}
```

**Fix:** Delete unused sites to free storage, or upgrade to the Hobby plan for 100 GB.

### File Path Invalid

```json theme={null}
{
  "error": "Invalid file path: ../etc/passwd",
  "code": "INVALID_PATH",
  "status": 400
}
```

**Fix:** File paths must be relative (no leading `/`), cannot contain `..`, and must be under 500 characters.

### Exposed Secret Detected During Publish

```json theme={null}
{
  "error": "We detected what looks like exposed secret material in files that would be publicly accessible after publish.",
  "code": "SECRET_DETECTED",
  "status": 400,
  "detectedSecrets": [
    {
      "path": "app.js",
      "type": "openai_api_key",
      "line": 22,
      "suggestedVariableName": "OPENAI_API_KEY",
      "suggestedProxyHosts": ["api.openai.com"]
    }
  ],
  "remediation": {
    "suggestedVariableNames": ["OPENAI_API_KEY"],
    "suggestedProxyHosts": ["api.openai.com"],
    "steps": [
      "Ask the user for permission to secure the key before publishing.",
      "Store the key as a site variable.",
      "Approve the required outbound host.",
      "Update only the published output to use the site proxy.",
      "Retry the publish."
    ]
  }
}
```

**Fix:** Do not publish the site as-is. Ask the user for permission, move the secret into [Site Variables](/api-reference/site-variables), approve the destination in [Proxy Host Allowlist](/api-reference/proxy-hosts), update only the published output to use the [Site Proxy](/api-reference/site-proxy), and then retry finalize.
