# How to Troubleshoot Common OpenSEO Errors: Complete Diagnostic Guide

> Troubleshoot common OpenSEO errors with our diagnostic guide. Learn to use the AppError class and API health endpoint for quick resolution. Fix your OpenSEO issues today.

- Repository: [Every App/open-seo](https://github.com/every-app/open-seo)
- Tags: how-to-guide
- Published: 2026-09-08

---

**OpenSEO errors are centrally handled through the `AppError` class in [`src/server/lib/errors.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/errors.ts) with typed error codes defined in [`src/shared/error-codes.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.ts), and can be diagnosed using the `/api/health` endpoint which aggregates setup checks from [`src/server/lib/setup-status.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/setup-status.ts).**

OpenSEO (every-app/open-seo) is built on a modular server-side architecture that centralizes error handling to help developers quickly identify configuration issues, authentication failures, and upstream provider problems. Understanding how to troubleshoot common OpenSEO errors requires familiarity with the typed error system and the health-check diagnostics built into the platform.

## Understanding OpenSEO's Error Architecture

OpenSEO implements a strict error-handling pattern that prevents internal stack traces from leaking while providing actionable feedback to developers.

### Centralized Error Handling in errors.ts

All runtime exceptions flow through [`src/server/lib/errors.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/errors.ts), which exports the **AppError** class. Every error is either thrown as an `AppError` (carrying a typed `ErrorCode`) or wrapped by the `asAppError` utility function. This ensures consistent error shapes throughout the application.

The `toClientError` function filters which error details reach the client. Only whitelisted codes like `AUTH_CONFIG_MISSING` return full messages; others return generic safe responses to prevent information leakage.

### Typed Error Codes with Zod Validation

The `errorCodeSchema` in [`src/shared/error-codes.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.ts) defines all valid error conditions using **Zod** validation. This guarantees that only predefined literals can be emitted, including:

- `UNAUTHENTICATED` and `FORBIDDEN` for auth failures
- `AUTH_CONFIG_MISSING` for missing DataForSEO credentials
- `RATE_LIMITED` for API throttling
- `VALIDATION_ERROR` for input issues
- `NOT_FOUND` for missing resources
- `INTERNAL_ERROR` for upstream provider failures

## Common OpenSEO Errors and Diagnostic Paths

When troubleshooting OpenSEO errors, match your symptom to the specific error code and source location.

### Authentication Failures (UNAUTHENTICATED / FORBIDDEN)

**Symptoms:** API calls return 401 or 403 status codes.

**Source:** Auth middleware in [`src/server/middleware.ts`](https://github.com/every-app/open-seo/blob/main/src/server/middleware.ts).

**Resolution:** Verify the request includes a valid `Authorization` header. Check the token-validation logic in the middleware to ensure the bearer token is correctly formatted and unexpired.

### Missing DataForSEO Credentials (AUTH_CONFIG_MISSING)

**Symptoms:** Health checks fail with `dataforseo:error` or API calls refuse to initialize.

**Source:** Setup check in [`src/server/lib/setup-status.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/setup-status.ts).

**Resolution:** Confirm the environment variable `DATAFORSEO_API_KEY` is correctly defined. See [`docs/DATAFORSEO_API_KEY.md`](https://github.com/every-app/open-seo/blob/main/docs/DATAFORSEO_API_KEY.md) for configuration instructions. The health endpoint will specifically flag this as `AUTH_CONFIG_MISSING` in its JSON response.

### Rate Limiting from DataForSEO (RATE_LIMITED)

**Symptoms:** HTTP 429 responses when fetching SERP data.

**Source:** DataForSEO client wrapper in [`src/server/lib/dataforseo/client.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/dataforseo/client.ts).

**Resolution:** Inspect the `Retry-After` header in the response and implement exponential backoff. The `AppError` thrown will include the code `RATE_LIMITED` when the upstream provider throttles requests.

### Validation Errors (VALIDATION_ERROR)

**Symptoms:** "Invalid domain" messages in the UI or API responses.

**Source:** Domain validation utilities in [`src/server/lib/domainUtils.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/domainUtils.ts).

**Resolution:** Ensure domain inputs are fully qualified URLs including the scheme (e.g., `https://example.com`). Partial domains or missing protocols trigger this error during the validation step.

### Rank Tracking Not Found (NOT_FOUND)

**Symptoms:** Rank-tracking configuration appears missing for existing projects.

**Source:** Rank-tracking services in `src/server/features/rank-tracking/services/*.ts`.

**Resolution:** Verify the project's rank-tracking entry exists in the database. For production debugging, run `pnpm exec wrangler tail` to view real-time Worker logs and confirm the record lookup logic.

### DataForSEO Internal Errors (INTERNAL_ERROR)

**Symptoms:** Task creation succeeds but results retrieval fails or returns malformed data.

**Source:** DataForSEO business logic in [`src/server/lib/dataforseo/business.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/dataforseo/business.ts).

**Resolution:** Check that the DataForSEO response JSON includes the required `task_id` field. Missing task IDs indicate upstream API issues or malformed request payloads.

## Using the Health Check Endpoint for Diagnostics

The `/api/health` route defined in [`src/routes/api/health.ts`](https://github.com/every-app/open-seo/blob/main/src/routes/api/health.ts) provides a centralized diagnostic interface for self-hosters. This endpoint calls `performSetupChecks()` from [`src/server/lib/setup-status.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/setup-status.ts) to verify:

- Database connectivity
- DataForSEO API key validity
- Cloudflare bindings
- Environment configuration completeness

The endpoint returns a concise JSON payload listing each component's status. Failing checks follow the pattern `service:error` (e.g., `database:error`, `dataforseo:error`), allowing you to pinpoint misconfigurations without digging through logs.

This health check runs the same validation logic as the Docker pre-flight script ([`scripts/selfhost-preflight.ts`](https://github.com/every-app/open-seo/blob/main/scripts/selfhost-preflight.ts)), ensuring consistency between startup checks and runtime diagnostics.

## Practical Error Handling Examples

### Safely Converting Errors for Client Responses

Use `toClientError` to prevent sensitive details from reaching the client:

```typescript
import { toClientError } from "@/server/lib/errors";

try {
  // Server logic that might throw
  await fetchSerpData(params);
} catch (e) {
  // Returns generic message unless error is in whitelist
  const safeError = toClientError(e);
  console.error(safeError);
  
  return new Response(safeError.message, { status: 500 });
}

```

### Implementing Custom Error Handling for DataForSEO

When integrating with the DataForSEO API, wrap responses in `AppError` instances:

```typescript
import { AppError } from "@/server/lib/errors";

async function fetchSerp(params: SerpParams) {
  const resp = await fetch(`https://api.dataforseo.com/serp`, {
    method: 'POST',
    headers: { 'Authorization': `Basic ${apiKey}` },
    body: JSON.stringify(params)
  });
  
  if (!resp.ok) {
    const body = await resp.json();
    // Throw typed error with upstream message
    throw new AppError("DATAFORSEO_AUTH_FAILED", body.message);
  }
  
  return resp.json();
}

```

### Health Check Implementation

The health endpoint implementation demonstrates how to aggregate system status:

```typescript
import { createFileRoute } from "@tanstack/react-router";
import { performSetupChecks } from "@/server/lib/setup-status";

export const Route = createFileRoute("/api/health")({
  loader: async () => {
    const checks = await performSetupChecks();
    return new Response(JSON.stringify(checks), {
      headers: { "Content-Type": "application/json" },
    });
  },
});

```

## Summary

- **Centralized error architecture**: All OpenSEO errors flow through [`src/server/lib/errors.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/errors.ts) as `AppError` instances with typed `ErrorCode` values from [`src/shared/error-codes.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.ts).
- **Safe client exposure**: The `toClientError` function filters error details to prevent leaking sensitive implementation details, exposing only whitelisted codes like `AUTH_CONFIG_MISSING`.
- **Health endpoint diagnostics**: The `/api/health` route aggregates setup checks from [`src/server/lib/setup-status.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/setup-status.ts), returning JSON that identifies failing components such as `dataforseo:error` or `database:error`.
- **Common error patterns**: Authentication issues originate in [`src/server/middleware.ts`](https://github.com/every-app/open-seo/blob/main/src/server/middleware.ts), validation errors in [`src/server/lib/domainUtils.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/domainUtils.ts), and DataForSEO-specific problems in [`src/server/lib/dataforseo/client.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/dataforseo/client.ts) and [`business.ts`](https://github.com/every-app/open-seo/blob/main/business.ts).
- **Self-hosting verification**: Use the health check endpoint alongside [`docs/SELF_HOSTING_DOCKER.md`](https://github.com/every-app/open-seo/blob/main/docs/SELF_HOSTING_DOCKER.md) and `pnpm exec wrangler tail` to diagnose configuration issues in production environments.

## Frequently Asked Questions

### How do I verify my DataForSEO API key is configured correctly?

Check the `/api/health` endpoint response for a `dataforseo:error` status. If present, verify that the `DATAFORSEO_API_KEY` environment variable is set according to [`docs/DATAFORSEO_API_KEY.md`](https://github.com/every-app/open-seo/blob/main/docs/DATAFORSEO_API_KEY.md). The health check runs the same validation as the startup pre-flight script, so a passing health check guarantees the key is valid.

### What does the UNAUTHENTICATED error mean in OpenSEO?

The `UNAUTHENTICATED` error code indicates missing or invalid bearer tokens in the `Authorization` header, thrown by the auth middleware in [`src/server/middleware.ts`](https://github.com/every-app/open-seo/blob/main/src/server/middleware.ts). Unlike `FORBIDDEN` (which indicates valid authentication but insufficient permissions), `UNAUTHENTICATED` means the request lacks valid credentials entirely.

### How can I safely expose error messages to the client without leaking stack traces?

Import `toClientError` from [`src/server/lib/errors.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/errors.ts) and wrap all caught exceptions before sending them to the client. This function only expands error messages for whitelisted codes (such as `AUTH_CONFIG_MISSING`), returning generic messages for all other errors to prevent exposing internal implementation details.

### Where should I look for logs when troubleshooting rank-tracking issues?

For rank-tracking `NOT_FOUND` errors originating in `src/server/features/rank-tracking/services/*.ts`, run `pnpm exec wrangler tail` to view real-time Worker logs. This shows the actual database queries and DataForSEO API responses, helping you determine whether the project record is missing or the upstream task failed to create.