How to Debug Issues in Open-SEO: A Complete Guide to Layered Error Handling

To debug issues in Open-SEO, trace errors through its five-layer architecture (Client, Server Functions, Middleware, Services, and Workflows) using centralized error handling, structured logging, and PostHog telemetry.

Open-SEO is an SEO automation platform built on a layered architecture that separates concerns across the client, server, and background jobs. Understanding how errors propagate through each layer—from React UI crashes to workflow failures—is essential for rapid diagnosis. This guide covers the exact file paths, middleware logic, and debugging commands you need to resolve issues efficiently.

Understanding Open-SEO's Five-Layer Architecture

Before debugging, identify which layer is failing:

Layer Responsibility Typical Failure Points
Client React UI, TanStack Query & Form UI crashes, network request failures, malformed input
Server Functions Exported functions called from the UI (src/serverFunctions/*.ts) Validation errors, third-party API failures
Middleware Global error handling and request preprocessing (src/middleware/*.ts) Uncaught exceptions, request parsing problems
Services Business-logic helpers (e.g., src/server/features/*/services/*.ts) Database issues, rate-limit handling, internal logic bugs
Workflows Long-running background jobs (src/server/workflows/*.ts) Cron failures, step-wise errors, workflow termination

Each layer has specific debugging protocols. Start by checking which boundary the error crosses.

Centralized Error Handling in errorHandlingMiddleware

All server-side code in Open-SEO runs through errorHandlingMiddleware located at src/middleware/errorHandling.ts. This middleware acts as the primary safety net for errors escaping from server functions and services.

The middleware executes the following logic:

  1. Wraps requests in a try/catch block to intercept thrown errors
  2. Detects validator errors (TanStack schema violations) and converts them to an AppError with code VALIDATION_ERROR (lines 28-42)
  3. Filters telemetry using shouldCaptureAppErrorCode from src/shared/error-codes.ts to decide whether the error should be reported to PostHog (lines 44-47)
  4. Captures telemetry by calling captureServerError to ship error data to src/server/lib/posthog.ts (line 19)
  5. Sanitizes output through toClientError to ensure the UI receives a safe JSON payload without sensitive internals (line 59)

If you see an error logged in Cloudflare Workers logs but no entry in PostHog, check NON_REPORTABLE_ERROR_CODES in src/shared/error-codes.ts. Common filtered codes include UNAUTHENTICATED and VALIDATION_ERROR.

Client-Side Debugging Strategies

When debugging the React frontend, verify three key areas:

  • Network inspector – Confirm the request URL, HTTP method, and payload match the server function signature
  • TanStack Query devtools – Inspect query keys, fetch status, and cached data for stale or missing values
  • React Error Boundaries – If the UI crashes completely, check the boundary's error prop for stack traces

Client errors originate from toClientError in src/server/lib/errors.ts. The response shape is predictable:

{
  "error": "VALIDATION_ERROR",
  "message": "...",
  "details": { ... }
}

To reproduce client issues locally, invoke the same server function via pnpm dev and watch the console output for the full stack trace.

Debugging Server Functions

Server functions live in src/serverFunctions/*.ts and serve as thin wrappers around business logic. They are validated by TanStack's schema before execution.

To debug a specific function in isolation:


# Run a single function directly

pnpm tsx src/serverFunctions/searchPerformance.ts <input-json>

If the function throws, the stack trace points to the underlying service (e.g., src/server/features/keywords/services/research/serp.ts).

Common server-side pitfalls:

  • Rate-limit errors – Look for errorCode: "RATE_LIMITED" in src/shared/error-codes.ts
  • DataForSEO authentication – Missing DATAFORSEO_API_KEY binding triggers DATAFORSEO_AUTH_FAILED

You can also test endpoints manually using curl:

curl -X POST http://localhost:5173/api/searchPerformance \
  -H "Content-Type: application/json" \
  -d '{"projectId":"abc","domain":"example.com"}'

If the response contains { "error": "VALIDATION_ERROR" }, the payload failed schema validation before reaching business logic.

Background Workflow Debugging

Workflows orchestrate multi-step jobs like site audits and rank checks using the TanStack Server Workflow framework. They reside in src/server/workflows/*.ts.

For example, SiteAuditWorkflow.ts logs failures at line 71:

console.error(`Audit ${auditId} failed:`, error);

To trace a broken workflow:

  1. Locate the specific workflow file (e.g., src/server/workflows/RankCheckWorkflow.ts)
  2. Search Cloudflare Workers logs for the audit ID or run ID
  3. Look for captureServerError calls, which include metadata like errorCode, method, and path

Trigger workflows manually during development:

pnpm tsx src/server/workflows/RankCheckWorkflow.ts --audit-id=12345

Logging and Observability Patterns

Every module in Open-SEO uses a dual-logging strategy: console.error for immediate debugging and captureServerError for aggregated observability.

Standard pattern from service files:

// Example from a service layer
try {
  await someAsyncOp();
} catch (err) {
  console.error("[rank-tracking] auto-metrics-refresh failed:", err);
  await captureServerError(err, { errorCode: "INTERNAL_ERROR" });
  throw err;
}

Search the repository for console.error( to find all hotspots (e.g., src/server/workflows/SiteAuditWorkflow.ts line 71).

PostHog integration (src/server/lib/posthog.ts) receives all non-filtered errors with these fields:

View these events in the PostHog UI under "Server Errors" and correlate timestamps with Cloudflare Workers logs.

Custom error handling in services should follow this pattern:

import { AppError } from "@/server/lib/errors";
import { captureServerError } from "@/server/lib/posthog";

export async function fetchBacklinks(projectId: string) {
  try {
    const res = await dataforseoClient.backlinks(projectId);
    return res;
  } catch (err) {
    console.error("backlinks.service error:", err);
    await captureServerError(err, { errorCode: "DATAFORSEO_AUTH_FAILED" });
    throw new AppError("BACKLINKS_BILLING_ISSUE", { projectId });
  }
}

Testing for Rapid Feedback

Open-SEO ships with Vitest for unit tests and Playwright for end-to-end testing. Failing tests often surface the same code paths that produce production errors.


# Fast unit tests

pnpm test

# Slow E2E UI tests

pnpm test:e2e

Investigate failing test files (e.g., src/shared/error-codes.test.ts) to understand expected error handling behavior and boundary conditions.

Summary

  • Use errorHandlingMiddleware (src/middleware/errorHandling.ts) to understand how server errors are caught, logged, and sanitized before reaching the client.
  • Check error-codes.ts to see if your error is in NON_REPORTABLE_ERROR_CODES, which explains missing PostHog entries.
  • Debug server functions by running them directly with pnpm tsx and inspecting the stack trace to locate service-level bugs.
  • Trace workflows by searching logs for the specific audit ID and examining console.error statements in src/server/workflows/*.ts.
  • Leverage dual logging with console.error for immediate feedback and captureServerError for production telemetry in src/server/lib/posthog.ts.
  • Validate fixes using the existing test suite to ensure error handling paths remain robust.

Frequently Asked Questions

How do I know if an error is being reported to PostHog?

Errors are only reported to PostHog if shouldCaptureAppErrorCode in src/shared/error-codes.ts returns true for the specific error code. If you see an error in Cloudflare Workers logs but not in PostHog, the code is likely listed in NON_REPORTABLE_ERROR_CODES (such as UNAUTHENTICATED or VALIDATION_ERROR). You can adjust the filtering logic after confirming the error's production impact.

What does a VALIDATION_ERROR response mean?

A VALIDATION_ERROR indicates that the request payload failed TanStack schema validation before reaching the server function's business logic. This error originates in src/middleware/errorHandling.ts (lines 28-42) and means your input JSON does not match the expected Zod or Valibot schema defined in the server function. Check the details field in the response to identify which specific fields failed validation.

How can I debug a failing background workflow?

Locate the specific workflow file in src/server/workflows/*.ts and search the Cloudflare Workers logs for the audit ID or run ID associated with the failed job. Look for console.error statements that log the specific error object, and check for calls to captureServerError which include metadata like errorCode and request path. You can also trigger workflows manually in development using pnpm tsx src/server/workflows/WorkflowName.ts --flag=value.

Where should I add custom error handling in my service code?

Add custom error handling in src/server/features/*/services/*.ts files by wrapping external API calls or database operations in try/catch blocks. Log immediate debugging information with console.error, then call captureServerError (from src/server/lib/posthog.ts) with the appropriate error code before re-throwing or wrapping the error in an AppError (defined in src/server/lib/errors.ts) to maintain the error handling contract expected by the middleware.

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 →