How to Debug OmniRoute: A Layer-by-Layer Debugging Guide
Enable APP_LOG_LEVEL=debug and OMNIROUTE_PROXY_FETCH_DEBUG=true, then trace requests by their UUID through the Dashboard's Traffic Inspector or command-line logs.
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, layered approach. This guide shows you how to debug an OmniRoute application using environment flags, UI toggles, and targeted source code inspection.
Debug Layers: What to Check and Where
OmniRoute's architecture spans nine distinct layers. Identify which layer your issue belongs to, then use the corresponding entry point and debug toggle.
| Layer | What to examine | Entry point | Debug toggle |
|---|---|---|---|
| Request-level logs | Request ID, timestamps, provider & model chosen, auth header sanitization | src/app/api/v1/**/route.ts (e.g., chat/completions/route.ts) |
APP_LOG_LEVEL=debug or UI "Debug Mode" toggle |
| Context manager | Per-request context with metadata (request ID, provider, combo, rate-limit info) | open-sse/contextManager.ts |
Automatic with APP_LOG_LEVEL=debug |
| Combo routing | Target resolution, strategy selection, fallback decisions | open-sse/services/combo.ts → resolveComboTargets() |
debugMode or Dashboard "Traffic Inspector" |
| Translator / Transformer | Request format conversion (OpenAI ↔ Anthropic, Gemini, etc.) and response back-translation | open-sse/translator/ and open-sse/transformer/ |
Dashboard → Translator playground |
| Executor | Provider-specific request building, headers, retries, proxy routing | open-sse/executors/ (e.g., default.ts, cursor.ts) |
OMNIROUTE_PROXY_FETCH_DEBUG=true |
| Guardrails | Prompt-injection guard, PII redaction, tool-source diagnostics | src/lib/guardrails/ |
"Tool-source diagnostics" toggle in Dashboard → Settings → Debug |
| MCP / A2A | Tool invocation logs, scope checks, JSON-RPC flow | open-sse/mcp-server/ and src/lib/a2a/ |
SQLite mcp_audit table via Traffic Inspector UI |
| Database / Persistence | Call-log trimming, quota snapshots, compression stats | src/lib/db/ modules |
Automatic; check for "too many SQL variables" errors |
| File-system artifacts | Legacy log files (${DATA_DIR}/log.txt, call_logs/) |
src/lib/db/healthCheck.ts |
Available when APP_LOG_LEVEL=debug |
Step-by-Step Debugging Workflow
1. Reproduce the Issue Locally
Start the development server with full debug output enabled:
export APP_LOG_LEVEL=debug
export OMNIROUTE_PROXY_FETCH_DEBUG=true
npm run dev
2. Capture the Request ID
Every request logs a UUID on its first line. Example:
[2024-01-15T10:23:45.123Z] req=123e4567-e89b-12d3-a456-426614174000 model=gpt-4o provider=openai
Copy this req= value—it's your tracing key through the entire pipeline.
3. Trace the Request Through the Pipeline
Grep the request ID to follow its path:
grep -R "req=123e4567-e89b-12d3-a456-426614174000" logs/
Expected log sequence:
- Context creation (
contextManager.ts) — initializes request metadata - Combo resolution (
combo.ts) — determines target providers and fallback order - Translator input/output (
translator/index.ts) — shows format conversion - Executor call (
executors/*) — provider-specific request execution - Final SSE stream or JSON response
4. Enable Fine-Grained Debug Modes
Beyond APP_LOG_LEVEL=debug, activate these for specific issues:
- UI Debug Mode: Settings → Advanced → Debug Mode toggle reveals hidden sidebar panels (Translator, Playground, Search Tools)
- Tool-source diagnostics: Dashboard → Settings → Debug → enable to label each tool with its originating module
- Proxy fetch debug:
OMNIROUTE_PROXY_FETCH_DEBUG=trueemits[ProxyFetch]lines for every proxy-relay call
5. Use Dashboard Inspection Tools
| Tool | Location | Purpose |
|---|---|---|
| Translator Playground | Dashboard → Translator | Paste OpenAI-style JSON, see exact upstream request, edit, and re-run |
| Traffic Inspector | Dashboard sidebar | Visualizes tool calls, combo targets, and proxy hops |
| Live Monitor | Dashboard | Streams raw SSE chunks from live requests in real time |
6. Check Guardrail Activity
Guardrail modules write concise [Guardrail] log lines when modifying or blocking payloads. Look for:
[Guardrail] PII_REDACTION_ENABLED=true, redacted 3 entities
[Guardrail] prompt-injection score=0.87, blocked
7. Verify Database Limits
If you encounter SQLite-related errors, run the relevant test to confirm bound-parameter limits are handled correctly:
npm run typecheck:core && node --import tsx/esm --test tests/unit/call-log-trim-sql-vars-5217.test.ts
The deleteCallLogRowsByIds function in src/lib/db/callLogs.ts now automatically chunks deletions to ≤500 per statement to avoid "too many SQL variables" errors.
Common Debugging Pitfalls and Fixes
| Symptom | Root cause | Fix |
|---|---|---|
| Empty response body | Provider returned no data, but executor suppressed the error | Set APP_LOG_LEVEL=debug; search for "[Executor] response empty" |
| Tool list truncated | Legacy MAX_TOOLS_LIMIT=128 applied |
Update to latest version—Opencode clients now receive full lists unless provider-specific limits apply (see chatcore/tools.ts) |
[ProxyEgress] shows proxy=direct incorrectly |
Proxy context not threaded through AsyncLocalStorage | Verify proxy pool configuration and enable APP_LOG_LEVEL=debug to see actual proxy name |
| SQLite "Too many SQL variables" | deleteCallLogRowsByIds attempted single IN (...) with >999 IDs |
Now batched automatically in src/lib/db/callLogs.ts |
| Unexpected PII redaction | PII_REDACTION_ENABLED or PII_RESPONSE_SANITIZATION=true |
Unset these env vars unless intentional masking required |
| MCP tool-routing confusion | Tool-source diagnostics disabled | Enable toggle; inspector will label each tool with origin module |
Code Examples for OmniRoute Debugging
Programmatic Request Tracing
Pass a custom request ID to correlate client and server logs:
import { v4 as uuidv4 } from "uuid";
const requestId = uuidv4(); // trace this through the pipeline
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" }]
}),
});
Query Stored Call Logs (SQLite)
sqlite3 ~/.omniroute/omniroute.db \
"SELECT id, request, response FROM call_logs WHERE request_id='123e4567-e89b-12d3-a456-426614174000';"
Enable Debug via UI Configuration
{
"debugMode": true,
"toolSourceDiagnostics": true
}
Set in Dashboard → Settings → Advanced, or pass as initialization config.
Key Source Files for Debugging
These files are your primary entry points when stepping through request flow or verifying wiring:
Summary
- Start with environment flags:
APP_LOG_LEVEL=debugandOMNIROUTE_PROXY_FETCH_DEBUG=truesurface the most detail with minimal configuration - Capture and grep the request ID: Every log line includes
req=UUIDfor end-to-end tracing - Use the Dashboard tools: Translator Playground, Traffic Inspector, and Live Monitor provide visual, interactive debugging
- Layer your investigation: Work from request logs → context → combo routing → translator → executor → guardrails → database
- Reference source files directly: The paths above link to the exact implementation in the OmniRoute repository
Frequently Asked Questions
How do I enable debug mode in OmniRoute?
Set APP_LOG_LEVEL=debug in your environment and start the server with npm run dev. Alternatively, enable the "Debug Mode" toggle in the Dashboard under Settings → Advanced. For proxy-specific issues, also set OMNIROUTE_PROXY_FETCH_DEBUG=true.
Where does OmniRoute store request logs?
Request logs appear in stdout when APP_LOG_LEVEL=debug is set. Persistent logs are stored in SQLite at ~/.omniroute/omniroute.db with the call_logs table. Legacy file-based logs exist in ${DATA_DIR}/log.txt and call_logs/ for backward compatibility.
How can I see which tool definition a tool originated from?
Enable "Tool-source diagnostics" in Dashboard → Settings → Debug. This toggle causes the Traffic Inspector to label each tool with its originating module. The MCP server also writes audit rows to the mcp_audit table for per-tool invocation history.
Why am I getting empty responses from OmniRoute?
Empty responses typically occur when a provider returns no data but the executor suppresses the error. Set APP_LOG_LEVEL=debug and search logs for "[Executor] response empty" to identify the failing provider call. Also verify that PII_REDACTION_ENABLED is not excessively redacting valid content.
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 →