# OpenSEO Error Codes: Complete Reference and Handling Guide

> Explore the OpenSEO error codes reference and handling guide. Learn to manage AppError instances, filter system issues, and map errors to HTTP responses for better application stability.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: api-reference
- Published: 2026-08-19

---

**OpenSEO defines a closed set of typed error codes in [`src/shared/error-codes.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.ts) that should be thrown as `AppError` instances, filtered through the `shouldCaptureAppErrorCode` helper to separate reportable system issues from expected user errors, and mapped to appropriate HTTP responses.**

OpenSEO is an open-source SEO platform that enforces a strict error taxonomy to keep telemetry clean and API responses predictable. Every available error code is declared in [`src/shared/error-codes.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.ts) and validated with a Zod enum. Understanding these codes and how to handle them is essential for building reliable integrations and maintaining healthy monitoring pipelines.

## Complete List of OpenSEO Error Codes

The following error codes represent every failure mode currently exposed by the platform:

- **`UNAUTHENTICATED`** — The request lacks valid authentication credentials. Typical scenario: missing or expired session token.
- **`AUTH_CONFIG_MISSING`** — Required authentication configuration is absent. Typical scenario: API keys not set.
- **`PAYMENT_REQUIRED`** — The account does not have an active payment method. Typical scenario: subscription expired.
- **`INSUFFICIENT_CREDITS`** — The account has run out of allotted credits. Typical scenario: exceeded usage quota.
- **`FORBIDDEN`** — The authenticated user is not allowed to perform the action. Typical scenario: permission restrictions.
- **`NOT_FOUND`** — The requested resource does not exist. Typical scenario: invalid IDs or deleted projects.
- **`AUDIT_CAPACITY_REACHED`** — The audit queue is full for the tenant. Typical scenario: too many concurrent audits.
- **`AUDIT_PAGE_LIMIT_EXCEEDED`** — The audit exceeds the page-count limit. Typical scenario: crawling a site that is too large.
- **`AUDIT_ALREADY_RUNNING`** — An audit is already in progress for the same target. Typical scenario: duplicate request.
- **`VALIDATION_ERROR`** — Input validation failed. Typical scenario: malformed request payloads.
- **`CRAWL_TARGET_BLOCKED`** — The crawler was blocked by robots.txt or HTTP 403. Typical scenario: target site disallows crawling.
- **`BACKLINKS_BILLING_ISSUE`** — Billing problem specific to the backlinks service. Typical scenario: account not provisioned for backlinks.
- **`AI_SEARCH_BILLING_ISSUE`** — Billing problem specific to the AI-search service. Typical scenario: account not provisioned for AI-search.
- **`DATAFORSEO_AUTH_FAILED`** — Authentication with the DataForSEO provider failed. Typical scenario: invalid DataForSEO credentials.
- **`RATE_LIMITED`** — The client hit a rate limit. Typical scenario: too many requests in a short window.
- **`UPSTREAM_UNAVAILABLE`** — An upstream dependency is unreachable. Typical scenario: external API outage.
- **`CONFLICT`** — A resource conflict occurred. Typical scenario: simultaneous updates.
- **`INTERNAL_ERROR`** — An unexpected server error. Typical scenario: unhandled exceptions or bugs.

## Reportable vs Non-Reportable Errors

OpenSEO splits its error taxonomy into reportable and non-reportable codes. The non-reportable set is stored in `NON_REPORTABLE_ERROR_CODES` inside [`src/shared/error-codes.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.ts) (lines 28-37). These codes represent expected, user-facing conditions such as authentication failures, quota limits, and validation problems. They are intentionally omitted from telemetry and crash reporting.

To decide whether an error should be captured, use the `shouldCaptureAppErrorCode` helper exported from the same file:

```ts
// src/shared/error-codes.ts
export function shouldCaptureAppErrorCode(
  code: ErrorCode | null | undefined,
): boolean {
  return code == null || !NON_REPORTABLE_ERROR_CODES.has(code);
}

```

If `shouldCaptureAppErrorCode` returns `true`, the error is forwarded to your monitoring pipeline. If it returns `false`, the error is treated as a known condition and ignored for reporting purposes.

## How to Handle OpenSEO Error Codes

### Throw Typed Errors with AppError

Server-side code should always raise an `AppError` (or a subclass) with one of the defined codes. This guarantees that downstream middleware can inspect the `code` property and choose the correct response.

```ts
// Example in a server function
import { AppError } from "@/server/lib/errors";

export async function getProject(id: string) {
  const project = await db.project.findUnique({ where: { id } });

  if (!project) {
    // NOT_FOUND is non-reportable; user sees a friendly message
    throw new AppError("NOT_FOUND", `Project ${id} does not exist`);
  }

  return project;
}

```

### Filter Errors at the Boundary

When catching errors in middleware or API handlers, extract the `code` property and pass it to `shouldCaptureAppErrorCode`. This prevents expected errors from polluting your exception tracker.

```ts
import { shouldCaptureAppErrorCode } from "@/shared/error-codes";

export async function errorHandlingMiddleware(ctx, next) {
  try {
    await next();
  } catch (err: any) {
    const code = err?.code as ErrorCode | undefined;

    if (shouldCaptureAppErrorCode(code)) {
      // Forward to telemetry (e.g., Sentry)
      await reportError(err);
    }

    // Convert to HTTP response
    ctx.response.status = err?.status ?? 500;
    ctx.response.body = { error: err.message, code };
  }
}

```

### Map Error Codes to HTTP Responses

Each code should be translated to an appropriate HTTP status. For example, `UNAUTHENTICATED` maps to `401`, `FORBIDDEN` to `403`, `NOT_FOUND` to `404`, `VALIDATION_ERROR` to `400`, and `INTERNAL_ERROR` to `500`. The `AppError` constructor in [`src/server/lib/errors.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/errors.ts) accepts a status code or infers one directly from the enum, so this mapping is typically handled automatically.

### Consume Errors on the Client

Front-end code should read the `code` field from the JSON error payload and branch accordingly. Redirect to login for `UNAUTHENTICATED`, show a friendly alert for `NOT_FOUND`, or surface a generic message for unexpected failures.

```ts
async function fetchProject(id: string) {
  const resp = await fetch(`/api/project/${id}`);
  const data = await resp.json();

  if (!resp.ok) {
    switch (data.code) {
      case "UNAUTHENTICATED":
        window.location.href = "/login";
        break;
      case "NOT_FOUND":
        alert("Project not found.");
        break;
      default:
        console.error("Unexpected error:", data);
    }
  }

  return data;
}

```

## Key Source Files for Error Handling

The following files define and implement the OpenSEO error taxonomy:

- **[`src/shared/error-codes.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.ts)** — Central definition of all error codes, the Zod schema, the `NON_REPORTABLE_ERROR_CODES` set, and the `shouldCaptureAppErrorCode` helper.
- **[`src/server/lib/errors.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/errors.ts)** — The `AppError` class used throughout server functions to attach an error code and HTTP status.
- **[`src/middleware/errorHandling.ts`](https://github.com/every-app/open-seo/blob/main/src/middleware/errorHandling.ts)** — Centralized middleware that applies `shouldCaptureAppErrorCode` to decide whether to forward errors to telemetry and formats HTTP responses.
- **`src/serverFunctions/*.ts`** — Example usages of `AppError` across rank-tracking, billing, and AI-search features.
- **[`src/shared/error-codes.test.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.test.ts)** — Unit tests confirming the behavior of `shouldCaptureAppErrorCode` and validation of the enum.

## Summary

- OpenSEO exposes a closed, Zod-validated set of error codes in [`src/shared/error-codes.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.ts).
- Always throw `AppError` (or subclasses) with a defined code so downstream code can inspect the failure mode.
- Use `shouldCaptureAppErrorCode` to keep expected user errors out of telemetry and focus alerting on abnormal conditions.
- Map each code to an appropriate HTTP status in your API layer.
- Read the `code` field in client-side fetch handlers to trigger specific UI flows.

## Frequently Asked Questions

### What is the difference between reportable and non-reportable OpenSEO error codes?

Reportable codes indicate unexpected or systemic problems that should be sent to telemetry, while non-reportable codes represent anticipated user-facing conditions such as authentication failures or quota limits. The `NON_REPORTABLE_ERROR_CODES` set in [`src/shared/error-codes.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.ts) (lines 28-37) lists the excluded codes, and the `shouldCaptureAppErrorCode` helper uses this set to filter them out.

### How does the `shouldCaptureAppErrorCode` function decide whether to capture an error?

The function returns `true` when the supplied code is `null`, `undefined`, or not present in `NON_REPORTABLE_ERROR_CODES`. If the code exists in that set, the function returns `false` and the error should be handled gracefully instead of forwarded to crash reporting.

### Which HTTP status should I return for a `VALIDATION_ERROR` in OpenSEO?

A `VALIDATION_ERROR` should map to HTTP `400 Bad Request`. The `AppError` constructor in [`src/server/lib/errors.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/errors.ts) can infer this status automatically from the error code enum, or you can supply it explicitly when instantiating the error.

### Where are OpenSEO error codes defined and tested?

All codes are defined in [`src/shared/error-codes.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.ts) alongside the Zod enum and `shouldCaptureAppErrorCode` helper. Their behavior is verified in [`src/shared/error-codes.test.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.test.ts), which confirms correct validation and the reportable-vs-non-reportable logic.