How to Debug OmniRoute: A Complete Guide to Multi-Layer Troubleshooting

To debug OmniRoute effectively, set APP_LOG_LEVEL=debug and OMNIROUTE_PROXY_FETCH_DEBUG=true, then trace the unique request ID through the pipeline from Next.js API routes to the executor layer using the Dashboard's Traffic Inspector and Translator Playground.

OmniRoute is a complex, modular router that stitches together dozens of LLM providers, streaming engines, and guardrails across multiple processes. Because components like the Next.js API routes, open-sse streaming workspace, MCP server, and MITM proxy run in different contexts, successful debugging requires a systematic, layered approach that tracks requests from ingress to upstream provider.

Understanding the Nine Debugging Layers

OmniRoute's architecture spans nine distinct layers where issues can originate. Debugging efficiently means knowing which layer to inspect based on your symptoms.

Layer 1: Request-Level Logs

The entry point for all API calls lives in src/app/api/v1/**/route.ts (e.g., chat/completions/route.ts). These routes generate the initial request ID and log authentication header sanitization. Set APP_LOG_LEVEL=debug in your environment to surface these logs, or enable the Debug Mode toggle in Dashboard → Settings → Advanced.

Layer 2: Context Manager

The open-sse/contextManager.ts module creates a per-request context object that carries metadata—including the request ID, target provider, combo configuration, and rate-limit info—throughout the pipeline. These logs appear automatically when APP_LOG_LEVEL is set to debug.

Layer 3: Combo Routing

The combo resolution logic in open-sse/services/combo.ts determines target resolution, strategy selection, and fallback decisions via the resolveComboTargets() function. Enable debugMode or use the Traffic Inspector tool in the Dashboard to see the ordered list of resolved targets for any given request.

Layer 4: Translator and Transformer

Format conversion between OpenAI, Anthropic, Gemini, and other providers happens in open-sse/translator/ and open-sse/transformer/. Use the Dashboard → Translator playground to see the raw upstream request and final downstream response for debugging translation mismatches.

Layer 5: Executor

Provider-specific request building, header construction, retries, and proxy routing occur in open-sse/executors/ (e.g., default.ts, cursor.ts). Set OMNIROUTE_PROXY_FETCH_DEBUG=true to emit [ProxyFetch] debug lines for every proxy-relay call, revealing the actual proxy name and egress details.

Layer 6: Guardrails

Prompt-injection protection and PII redaction operate from src/lib/guardrails/. These modules write concise [Guardrail] log lines when modifying or blocking payloads. Enable the Tool-source diagnostics toggle in Dashboard → Settings → Debug to see which tool definition each tool originated from.

Layer 7: MCP and A2A

Tool invocation logs, scope checks, and JSON-RPC flow are handled by open-sse/mcp-server/ and src/lib/a2a/. The MCP server writes per-tool audit rows to the SQLite mcp_audit table, viewable via the Traffic Inspector UI.

Layer 8: Database and Persistence

Call-log trimming, quota snapshots, and compression stats are managed in src/lib/db/ modules. If you encounter "deleteCallLogRowsByIds too many SQL variables" errors, verify that the log-trimmer in src/lib/db/callLogs.ts is chunking deletions automatically (≤500 per statement).

Layer 9: File-System Artifacts

Legacy log files in ${DATA_DIR}/log.txt and call_logs/ directories provide backward-compatible raw request/response dumps when APP_LOG_LEVEL=debug is enabled. These are maintained by src/lib/db/healthCheck.ts.

Step-by-Step Debugging Workflow

Follow this systematic approach to isolate issues in OmniRoute:

  1. Reproduce the issue locally by running the dev server with npm run dev.

  2. Identify the request ID from the first line of every request log, which contains a UUID like req=xxxx.

  3. Trace through the pipeline by grepping the request ID in logs to see context creation (contextManager), combo resolution (combo.ts), translator input/output (translator/index.ts), and executor calls (executors/*).

  4. Enable fine-grained debug:

    • Set APP_LOG_LEVEL=debug to surface all internal pino logs.
    • Set OMNIROUTE_PROXY_FETCH_DEBUG=true for low-level fetch diagnostics.
    • Activate the UI Debug Mode toggle to reveal hidden sidebar panels (Translator, Playground, Search Tools).
  5. Use Dashboard tools:

    • Translator Playground: Paste a request payload to see the exact upstream request, edit parameters, and re-run.
    • Traffic Inspector: Visualizes each tool call, combo target, and proxy hop.
    • Live Monitor: Streams raw SSE chunks from live requests for real-time inspection.
  6. Check guardrail logs for [Guardrail] entries indicating modified or blocked payloads.

  7. Inspect the database using SQLite queries or run specific tests like npm run typecheck:core && node --import tsx/esm --test tests/unit/call-log-trim-sql-vars-5217.test.ts to verify SQLite limits.

Common Pitfalls and Quick Fixes

Symptom Likely Cause Quick Fix
Empty response body Provider returned no data but executor suppressed the error due to insufficient logging. Set APP_LOG_LEVEL=debug and look for "[Executor] response empty" lines.
Tool list truncation Legacy hard-coded MAX_TOOLS_LIMIT=128. Update to the latest version where chatcore/tools.ts detects Opencode clients and only truncates when provider-specific limits are known.
Missing proxy information [ProxyEgress] logged proxy=direct incorrectly. Ensure proxy context is threaded through AsyncLocalStorage and APP_LOG_LEVEL=debug is set to see the actual proxy name in [ProxyEgress] lines.
SQLite "Too many SQL variables" deleteCallLogRowsByIds attempted single IN (...) with >999 IDs. The truncation logic in src/lib/db/callLogs.ts now batches deletions (≤500 per statement).
Unexpected guardrail redaction PII_REDACTION_ENABLED or PII_RESPONSE_SANITIZATION set to true. Verify these env vars in src/lib/guardrails/pii-masker.ts are unset unless intentional.
MCP tool-routing confusion Tool-source diagnostics disabled. Enable the Tool-source diagnostics toggle; the inspector will label each tool with its originating module.

Essential Debug Configuration

Configure your environment for comprehensive debugging with these settings:

export APP_LOG_LEVEL=debug
export OMNIROUTE_PROXY_FETCH_DEBUG=true
npm run dev

Fetch a specific request's logs by ID:

grep -R "req=123e4567-e89b-12d3-a456-426614174000" logs/

Programmatically trace requests with custom IDs:

import { v4 as uuidv4 } from "uuid";

const requestId = uuidv4();
fetch("http://localhost:3000/api/v1/chat/completions", {
  method: "POST",
  headers: {
    "Content-Type": "application/json",
    "x-omniroute-request-id": requestId,
  },
  body: JSON.stringify({ 
    model: "gpt-4o", 
    messages: [{ role: "user", content: "debug me" }] 
  }),
});

Inspect call logs directly in SQLite:

sqlite3 ~/.omniroute/omniroute.db "SELECT id, request, response FROM call_logs WHERE request_id='123e4567-e89b-12d3-a456-426614174000';"

Key Source Files for Debugging

When stepping through the request flow, reference these specific files in the OmniRoute repository:

Summary

  • OmniRoute debugging requires a layered approach spanning nine architectural levels from API entry points to database persistence.
  • Always obtain the request ID first to trace execution through contextManager, combo.ts, translators, and executors.
  • Enable APP_LOG_LEVEL=debug for comprehensive logging and OMNIROUTE_PROXY_FETCH_DEBUG=true for network-level diagnostics.
  • Use the Dashboard tools—Traffic Inspector, Translator Playground, and Live Monitor—for visual debugging of combo routing and translations.
  • Reference specific source files like open-sse/executors/base.ts and src/lib/db/callLogs.ts when investigating executor behavior or database errors.

Frequently Asked Questions

How do I enable debug mode in OmniRoute?

Set the environment variable APP_LOG_LEVEL=debug before starting the server, and toggle the Debug Mode switch in the Dashboard under Settings → Advanced. This combination surfaces internal pino logs and reveals hidden debugging panels in the UI, including the Translator Playground and Traffic Inspector.

Where can I find the request ID to trace a specific call?

The request ID appears as a UUID in the first line of every request log (format: req=xxxx). You can also generate and pass your own request ID using the x-omniroute-request-id header when making API calls, then grep for this ID in the logs or query the SQLite call_logs table directly.

Why am I seeing "Too many SQL variables" errors in the logs?

This occurs when the call-log trimming logic attempts to delete too many rows at once (historically >999 IDs in a single IN clause). The fix is implemented in src/lib/db/callLogs.ts, which now automatically chunks deletions into batches of 500 or fewer. Update to the latest version to resolve this SQLite limitation.

How do I debug why a tool is not appearing or being truncated?

Check the Tool-source diagnostics toggle in Dashboard → Settings → Debug to see which module originated each tool definition. If tools are truncated, verify you're running the latest version where chatcore/tools.ts detects Opencode clients and only applies truncation when provider-specific limits are known, rather than the legacy hard-coded MAX_TOOLS_LIMIT=128.

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 →