How to Debug OmniRoute: A Layered Approach to Troubleshooting LLM Router Issues
To debug OmniRoute, enable APP_LOG_LEVEL=debug and trace requests through the nine-layer architecture—from API entry points to the MCP server—using request IDs to correlate logs across the Context Manager, Combo Routing engine, and Executor layers.
OmniRoute is a complex, modular router that stitches together dozens of LLM providers, a streaming engine, combo-routing logic, guardrails, and a full-stack UI. Because many components run in different processes (Next.js API routes, the open-sse streaming workspace, the MCP server, and the MITM proxy), successful debugging requires a systematic approach that leverages environment variables, UI toggles, and specific source files in the diegosouzapw/OmniRoute repository.
The Nine Layers of OmniRoute Debugging
OmniRoute's architecture spans nine distinct layers. When you debug OmniRoute issues, work through these layers from the request entry point down to persistence and artifacts.
1. Request-Level Logs
Every request generates a UUID (e.g., req=xxxx) logged in src/app/api/v1/**/route.ts files such as chat/completions/route.ts. These logs capture timestamps, the selected provider and model, and sanitized auth headers.
To enable detailed output, set APP_LOG_LEVEL=debug in your environment or toggle Debug Mode in the UI under Settings → Advanced.
2. Context Manager
The per-request context object holds metadata including the request ID, provider, combo configuration, and rate-limit info. Located in open-sse/contextManager.ts, this layer automatically logs context creation when APP_LOG_LEVEL is set to debug.
3. Combo Routing
This layer handles target resolution, strategy selection, and fallback decisions. The key function is resolveComboTargets() in open-sse/services/combo.ts. Enable the Traffic Inspector tool in the Dashboard to see the ordered list of resolved targets, or toggle debugMode to surface these logs.
4. Translator and Transformer
Request format conversion between OpenAI, Anthropic, Gemini, and other formats happens in open-sse/translator/ and open-sse/transformer/. Use the Dashboard → Translator playground to view raw upstream requests and final downstream responses for debugging translation mismatches.
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.
6. Guardrails
Prompt-injection guards, PII redaction, and tool-source diagnostics reside in src/lib/guardrails/. Enable "Tool-source diagnostics" in Dashboard → Settings → Debug to see which module originated each tool definition. Guardrail modules write concise [Guardrail] lines when modifying or blocking payloads.
7. MCP and A2A
Tool invocation logs, scope checks, and JSON-RPC flow are managed in 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.
8. Database and Persistence
Call-log trimming, quota snapshots, and compression stats are handled in src/lib/db/ modules. If you encounter "deleteCallLogRowsByIds too many SQL variables" errors, note that the log-trimmer in src/lib/db/callLogs.ts now chunks deletions automatically to avoid SQLite limits.
9. File-System Artifacts
Legacy log files in ${DATA_DIR}/log.txt and call_logs/ directories (managed by src/lib/db/healthCheck.ts) contain raw request/response dumps when APP_LOG_LEVEL=debug is enabled for backward compatibility.
Step-by-Step Debugging Workflow
Follow this systematic workflow to diagnose issues in OmniRoute:
-
Reproduce the issue locally by running the dev server with
npm run dev. -
Identify the request ID from the first line of every request log, which contains a UUID like
req=xxxx. -
Trace through the pipeline by grepping the request ID in logs to observe:
- Context creation in
contextManager - Combo resolution via
combo.ts - Translator input/output in
translator/index.ts - Executor calls in
executors/* - Final SSE stream or JSON response
- Context creation in
-
Enable fine-grained debugging:
- Set
APP_LOG_LEVEL=debugfor all internalpinologs - Set
OMNIROUTE_PROXY_FETCH_DEBUG=truefor low-level fetch diagnostics - Activate the UI "Debug Mode" toggle to reveal hidden sidebar panels (Translator, Playground, Search Tools)
- Set
-
Use Dashboard tools:
- Translator Playground: Paste request payloads to see exact upstream requests and edit them
- Traffic Inspector: Visualizes tool calls, combo targets, and proxy hops
- Live Monitor: Streams raw SSE chunks for real-time inspection
-
Check guardrail logs for
[Guardrail]entries indicating payload modifications. -
Inspect the database by running tests like
npm run typecheck:core && node --import tsx/esm --test tests/unit/call-log-trim-sql-vars-5217.test.tsto verify SQLite bound-param limits are not exceeded.
Common Issues and Quick Fixes
| Symptom | Likely Cause | Quick Fix |
|---|---|---|
| Empty response body | Provider returned no data but executor suppressed the error | Set APP_LOG_LEVEL=debug and look for [Executor] response empty lines |
| Tool list truncated | Legacy hard-coded MAX_TOOLS_LIMIT=128 |
Update to the latest version which detects Opencode clients and only truncates when provider-specific limits are known (see chatcore/tools.ts) |
| Missing proxy information | [ProxyEgress] logged proxy=direct incorrectly |
Ensure proxy pool is configured and APP_LOG_LEVEL=debug is set to see actual proxy names via AsyncLocalStorage-threaded context |
| SQLite "Too many SQL variables" | deleteCallLogRowsByIds attempted single IN (...) with >999 IDs |
The truncation logic now batches deletions (≤500 per statement) automatically |
| Unexpected guardrail redaction | PII_REDACTION_ENABLED or PII_RESPONSE_SANITIZATION set to true |
Verify these env vars are unset unless intentional |
| MCP tool-routing confusion | Tool-source diagnostics disabled | Enable the "Tool-source diagnostics" toggle to label each tool with its originating module |
Essential Debug Commands and Configuration
Enable full debug output from the command line:
export APP_LOG_LEVEL=debug
export OMNIROUTE_PROXY_FETCH_DEBUG=true
npm run dev
Fetch a request's log by ID using Unix grep:
grep -R "req=123e4567-e89b-12d3-a456-426614174000" logs/
Programmatically trace requests in Node.js:
import { v4 as uuidv4 } from "uuid";
const requestId = uuidv4(); // pass this header downstream
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 stored call logs via SQLite:
sqlite3 ~/.omniroute/omniroute.db "SELECT id, request, response FROM call_logs WHERE request_id='123e4567-e89b-12d3-a456-426614174000';"
Enable tool-source diagnostics via UI settings:
{
"debugMode": true,
"toolSourceDiagnostics": true
}
Key Source Files for Debugging
When you need to step through code or verify wiring, consult these specific files in the diegosouzapw/OmniRoute repository:
src/app/api/v1/chat/completions/route.ts– API entry point for request validation and routing toopen-sseopen-sse/services/combo.ts– ContainsresolveComboTargets()for combo resolution and fallback logicopen-sse/translator/index.ts– Format conversion pipelines for debugging translation mismatchesopen-sse/executors/base.ts– Retry logic, header building, and proxy handlingsrc/lib/guardrails/pii-masker.ts– Redaction behavior controlled byPII_REDACTION_ENABLEDopen-sse/mcp-server/server.ts– Tool registration, audit logging, and scope enforcementsrc/mitm/server.cjs– MITM proxy for TLS/HTTPS network-level inspectionsrc/lib/db/callLogs.ts– Handles SQLite bound-param limits via chunked deletionsdocs/reference/ENVIRONMENT.md– Complete list of debug togglessrc/app/dashboard/settings/page.tsx– Implementation of the Debug Mode UI toggle
Summary
- OmniRoute debugging operates across nine layers from API routes to file-system artifacts, each with specific entry points in
src/app/api/v1/,open-sse/, andsrc/lib/directories. - Request IDs are the primary correlation mechanism—grep these UUIDs across logs to trace the full pipeline from
contextManagerthroughcombo.tsto the executors. - Environment variables
APP_LOG_LEVEL=debugandOMNIROUTE_PROXY_FETCH_DEBUG=truesurface internalpinologs and proxy-relay diagnostics. - Dashboard tools including the Translator Playground and Traffic Inspector provide visual debugging without code changes.
- Common pitfalls like SQLite variable limits and tool truncation have specific fixes in
src/lib/db/callLogs.tsandchatcore/tools.tsrespectively.
Frequently Asked Questions
How do I find which component is causing an error in OmniRoute?
Start by locating the request ID in the initial log line, then grep for that ID across your logs to trace the execution flow. Check open-sse/contextManager.ts for context creation, open-sse/services/combo.ts for routing decisions, and open-sse/executors/ for provider-specific errors. Enable APP_LOG_LEVEL=debug to see detailed [Executor] and [Guardrail] log lines that indicate exactly where the pipeline stops.
What environment variables should I set to debug OmniRoute effectively?
Set APP_LOG_LEVEL=debug to enable all internal logging via pino, and OMNIROUTE_PROXY_FETCH_DEBUG=true to see [ProxyFetch] debug lines for proxy relay calls. For PII-related issues, check that PII_REDACTION_ENABLED and PII_RESPONSE_SANITIZATION are explicitly set to false unless you require data masking. These variables are documented in docs/reference/ENVIRONMENT.md.
How can I debug request translation issues between LLM providers?
Use the Translator Playground in the Dashboard. Navigate to Dashboard → Translator, paste your client-side OpenAI-style JSON payload, and click Convert to see the exact upstream request sent to providers like Anthropic or Gemini. The playground shows both the transformed request and the raw response, helping identify format mismatches in open-sse/translator/index.ts.
Where are tool invocation logs stored in OmniRoute?
Tool invocation logs and JSON-RPC flows are stored in the mcp_audit SQLite table by the MCP server in open-sse/mcp-server/server.ts. You can view these entries via the Traffic Inspector UI or query them directly using sqlite3 ~/.omniroute/omniroute.db with SQL statements filtering by request_id. Enable "Tool-source diagnostics" in the Dashboard settings to see which module originated each tool definition.
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:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →