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

> Debug Open-SEO issues effectively by tracing errors across its five-layer architecture. Learn to use centralized error handling, structured logging, and PostHog telemetry for streamlined debugging.

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

---

**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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/src/server/lib/errors.ts). The response shape is predictable:

```json
{
  "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:

```bash

# 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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.ts)
- **DataForSEO authentication** – Missing `DATAFORSEO_API_KEY` binding triggers `DATAFORSEO_AUTH_FAILED`

You can also test endpoints manually using curl:

```bash
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`](https://github.com/every-app/open-seo/blob/main/SiteAuditWorkflow.ts) logs failures at line 71:

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

```

To trace a broken workflow:

1. Locate the specific workflow file (e.g., [`src/server/workflows/RankCheckWorkflow.ts`](https://github.com/every-app/open-seo/blob/main/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:

```bash
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:

```ts
// 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`](https://github.com/every-app/open-seo/blob/main/src/server/workflows/SiteAuditWorkflow.ts) line 71).

**PostHog integration** ([`src/server/lib/posthog.ts`](https://github.com/every-app/open-seo/blob/main/src/server/lib/posthog.ts)) receives all non-filtered errors with these fields:

- `errorCode` (from [`src/shared/error-codes.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.ts))
- Request metadata (`method`, `path`)
- Optional `details` supplied by the `AppError`

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:

```ts
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.

```bash

# Fast unit tests

pnpm test

# Slow E2E UI tests

pnpm test:e2e

```

Investigate failing test files (e.g., [`src/shared/error-codes.test.ts`](https://github.com/every-app/open-seo/blob/main/src/shared/error-codes.test.ts)) to understand expected error handling behavior and boundary conditions.

## Summary

- **Use `errorHandlingMiddleware`** ([`src/middleware/errorHandling.ts`](https://github.com/every-app/open-seo/blob/main/src/middleware/errorHandling.ts)) to understand how server errors are caught, logged, and sanitized before reaching the client.
- **Check [`error-codes.ts`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/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`](https://github.com/every-app/open-seo/blob/main/src/server/lib/errors.ts)) to maintain the error handling contract expected by the middleware.