OpenSEO Error Codes and Debugging Common Failures: Complete Guide

OpenSEO uses a strict, type-safe error code system defined in error-codes.ts to categorize every predictable failure, with helper functions like shouldCaptureAppErrorCode controlling telemetry capture and isErrorCode enabling runtime validation.

The OpenSEO codebase implements a centralized error handling architecture that makes debugging predictable and telemetry noisy-free. Every error that can surface from the backend or client libraries is enumerated explicitly, with clear rules about which failures warrant alerting and which represent expected user-side issues.


The Complete OpenSEO Error Code Reference

All error codes live in a single source of truth: [src/shared/error-codes.ts](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.ts). The ERROR_CODES constant enumerates every possible failure state.

Error Code Typical Cause Immediate Debug Action
UNAUTHENTICATED Missing or invalid auth token Verify API key or session token exists and hasn't expired
AUTH_CONFIG_MISSING Required configuration absent Check .env or runtime config for required variables like DATAFORSEO_API_KEY
PAYMENT_REQUIRED Account lacks paid plan Review billing status in dashboard; upgrade if necessary
INSUFFICIENT_CREDITS API quota exhausted Monitor credits UI; add credits or wait for quota reset
FORBIDDEN Insufficient permissions Confirm user access rights to project or domain
NOT_FOUND Requested entity doesn't exist Double-check IDs, URLs, and slugs for typos
AUDIT_CAPACITY_REACHED System-wide audit queue full Retry later or contact support for capacity issues
AUDIT_PAGE_LIMIT_EXCEEDED Audit exceeds page count limits Reduce page scope or split into smaller audits
AUDIT_ALREADY_RUNNING Duplicate audit in progress Wait for existing audit completion; implement deduplication
VALIDATION_ERROR Input fails schema validation Inspect Zod validation details in response body
CRAWL_TARGET_BLOCKED Target blocks crawler Review target's robots.txt and firewall rules
BACKLINKS_BILLING_ISSUE Billing problem specific to backlinks Resolve in Backlinks billing section
AI_SEARCH_BILLING_ISSUE Billing problem specific to AI search Resolve in AI Search billing section
DATAFORSEO_AUTH_FAILED Invalid DataForSEO credentials Re-enter credentials; test with direct API call
RATE_LIMITED Too many requests Implement exponential backoff; respect Retry-After header
UPSTREAM_UNAVAILABLE Third-party service down Check upstream status page; implement circuit breaker
CONFLICT Resource conflict or duplicate Adjust request payload to resolve collision
INTERNAL_ERROR Unexpected server failure Capture stack trace; report to telemetry if reportable

How Error Codes Drive Telemetry and UX

OpenSEO distinguishes between reportable errors (unexpected failures needing attention) and non-reportable errors (expected user-side issues). 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) includes codes like UNAUTHENTICATED and NOT_FOUND that don't indicate system problems.

The shouldCaptureAppErrorCode Gate

The shouldCaptureAppErrorCode function determines whether an error should reach PostHog:

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

This single function controls error reporting across three critical integration points:


Runtime Validation with isErrorCode

Type safety doesn't stop at compile time. The isErrorCode type guard validates unknown strings against the schema:

// src/shared/error-codes.ts (excerpt)
export const errorCodeSchema = z.enum(ERROR_CODES);
export type ErrorCode = z.infer<typeof errorCodeSchema>;

export function isErrorCode(value: unknown): value is ErrorCode {
  return errorCodeSchema.safeParse(value).success;
}

Use this defensively when processing API responses or external data that might carry malformed error codes.


Practical Debugging Patterns

Pattern 1: Safe API Response Handling

import { isErrorCode, shouldCaptureAppErrorCode } from "@/shared/error-codes";

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

  if (!res.ok && isErrorCode(data.error?.code)) {
    const code = data.error.code;
    
    switch (code) {
      case "NOT_FOUND":
        console.warn("Project not found — verify the ID");
        return null;
        
      case "RATE_LIMITED": {
        const waitSeconds = Number(res.headers.get("Retry-After")) || 5;
        await new Promise(r => setTimeout(r, waitSeconds * 1000));
        return fetchProject(id); // retry
      }
        
      case "UNAUTHENTICATED":
        // Expected user error — don't spam telemetry
        redirectToLogin();
        return;
        
      default:
        if (shouldCaptureAppErrorCode(code)) {
          posthog.capture("api_error", { 
            code, 
            endpoint: `/api/projects/${id}`,
            trace: data.error?.stack
          });
        }
        throw new Error(`API failed: ${code}`);
    }
  }
  
  return data;
}

Pattern 2: Server-Side Error Construction

import { errorCodeSchema, type ErrorCode } from "@/shared/error-codes";

export class AppError extends Error {
  constructor(
    public readonly code: ErrorCode,
    message?: string
  ) {
    super(message ?? errorCodeSchema.Enum[code]);
    this.name = "AppError";
  }
  
  toJSON() {
    return {
      code: this.code,
      message: this.message,
      // Omit stack in production responses
      ...(process.env.NODE_ENV === "development" && { stack: this.stack })
    };
  }
}

// Route handler usage
app.get("/api/audits/:id", async (req, res, next) => {
  const audit = await getAudit(req.params.id);
  
  if (!audit) {
    throw new AppError("NOT_FOUND", `Audit ${req.params.id} not found`);
  }
  
  if (audit.userId !== req.user.id) {
    throw new AppError("FORBIDDEN");
  }
  
  if (await isAuditCapacityReached()) {
    throw new AppError("AUDIT_CAPACITY_REACHED");
  }
  
  res.json(audit);
});

Pattern 3: Telemetry Integration in Error Middleware

// src/middleware/errorHandling.ts (conceptual)
import { shouldCaptureAppErrorCode } from "@/shared/error-codes";

export function errorHandler(err: Error, req: Request, res: Response, next: NextFunction) {
  if (err instanceof AppError) {
    const shouldCapture = shouldCaptureAppErrorCode(err.code);
    
    if (shouldCapture) {
      posthog.captureException(err, {
        distinct_id: req.user?.id,
        properties: {
          code: err.code,
          path: req.path,
          method: req.method
        }
      });
    }
    
    // Always log to structured logs regardless of capture decision
    logger.log(shouldCapture ? "error" : "info", "Request failed", {
      code: err.code,
      message: err.message,
      reportable: shouldCapture
    });
    
    return res.status(mapCodeToStatus(err.code)).json(err.toJSON());
  }
  
  // Unhandled errors always captured
  posthog.captureException(err);
  next(err);
}

Debugging Workflow for Common Failures

  1. Extract the code — Inspect error.code from the JSON response body, not just HTTP status

  2. Validate with isErrorCode — Defensively confirm the string is a recognized member of the enum before branching logic

  3. Consult the reportability check — Call shouldCaptureAppErrorCode(code) to understand if this failure indicates a system issue worth escalating

  4. Apply targeted remediation:

    • Billing errors (PAYMENT_REQUIRED, *_BILLING_ISSUE) → Dashboard billing section
    • Quota errors (INSUFFICIENT_CREDITS, RATE_LIMITED) → Backoff and retry with headers
    • Validation errors → Parse error.details for field-level Zod failures
    • Upstream errors → Check DataForSEO status page, implement circuit breaker
  5. Verify with the test suite — Run [src/shared/error-codes.test.ts](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.test.ts) to confirm error code behavior matches expectations after any code changes


Key Files and Their Roles

File Purpose Direct Link
src/shared/error-codes.ts Central enum, schema, and helper functions View source
src/shared/error-codes.test.ts Unit tests for enum coverage and reportability logic View source
src/middleware/errorHandling.ts Express middleware normalizing errors and gating telemetry View source
src/server/mcp/instrumentation.ts Metrics instrumentation using shouldCaptureAppErrorCode View source
src/client/lib/error-messages.ts Client-side error message resolution View source
src/server/lib/errors.ts Server-side error class definitions View source

Summary

  • OpenSEO error codes are exhaustively enumerated in error-codes.ts with 18 distinct failure categories
  • shouldCaptureAppErrorCode separates user errors from system problems, keeping PostHog alerts actionable
  • isErrorCode provides runtime type safety for defensive programming
  • Debugging follows a four-step flow: extract → validate → check reportability → apply targeted fix
  • The test suite in error-codes.test.ts prevents regression in error handling behavior

Frequently Asked Questions

How do I add a new error code to OpenSEO?

Add the string literal to the ERROR_CODES array in [src/shared/error-codes.ts](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.ts), then decide its reportability by adding or omitting it from NON_REPORTABLE_ERROR_CODES. Update [error-codes.test.ts](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.test.ts) with assertions for the new code, and regenerate any TypeScript types if needed. The Zod schema automatically includes new enum members.

Why am I seeing INTERNAL_ERROR in my logs instead of a specific code?

INTERNAL_ERROR is the catch-all for unhandled exceptions that don't originate from explicit AppError throws. This typically indicates a bug or unexpected state. Check the stack trace, ensure all error paths construct AppError with appropriate codes, and verify your middleware is catching and re-throwing with proper categorization.

How can I suppress specific errors from appearing in PostHog?

Add the error code to NON_REPORTABLE_ERROR_CODES in error-codes.ts. The shouldCaptureAppErrorCode function will automatically exclude it from telemetry across all instrumentation points. Be selective—only suppress errors that represent normal user behavior, not system degradation.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →