How OmniRoute Prevents Error Stack Exposure: A Complete Security Implementation
OmniRoute enforces Hard Rule #12 by centralizing error sanitization in open-sse/utils/error.ts, ensuring no raw stack traces, absolute file paths, or sensitive credentials leak to clients through the sanitizeErrorMessage and buildErrorBody utilities.
OmniRoute (diegosouzapw/OmniRoute) implements strict security controls to prevent error stack exposure, a critical vulnerability that can reveal server internals, file-system layouts, and sensitive data to attackers. The framework mandates Hard Rule #12, which requires that raw error messages and stack traces must never reach client responses. This article examines the centralized sanitization architecture that enforces this rule across all API routes, Server-Sent Events (SSE) streams, and Model Context Protocol (MCP) server endpoints.
Centralized Error Sanitization Architecture
All error handling in OmniRoute funnels through a single source of truth located in open-sse/utils/error.ts. This module exports two primary functions that transform dangerous raw errors into safe, client-ready messages.
The sanitizeErrorMessage Function
The sanitizeErrorMessage(message) function accepts any input value—whether a string, Error object, undefined, or otherwise—and returns a sanitized, single-line string. According to the source code in diegosouzapw/OmniRoute, this function implements four critical security behaviors:
- Stack trace truncation: The function keeps only the first line of any input, automatically discarding subsequent stack-trace lines that would reveal internal call hierarchies.
- Path redaction: It identifies and removes absolute file-system paths (such as
/home/user/project/file.ts:10orC:\Users\admin\app\index.js) and replaces them with the placeholder<path>. - Credential scrubbing: The function redacts sensitive fragments including Bearer tokens, JSON fields like
apiKey,access_token, andclient_secret, plus data-URL images that might contain embedded secrets. - Input normalization: Non-string inputs are handled safely—
undefinedandnullbecome empty strings, numbers convert to their string representation, andErrorobjects reduce to"Error: <message>".
Additionally, the implementation includes a lightweight regular-expression guard that prevents pathologically long inputs from triggering ReDoS (Regular Expression Denial of Service) slowdowns, ensuring the sanitization remains performant under adversarial conditions.
The buildErrorBody Wrapper
The buildErrorBody(error) function consumes the output of sanitizeErrorMessage and wraps it into a consistent JSON error payload for HTTP and SSE responses. All API routes import this helper and return standardized error objects using new Response(buildErrorBody(err)). This guarantees a uniform error format across the entire application surface while maintaining the security boundaries established by the sanitization layer.
Enforced Usage Across the Codebase
OmniRoute treats error sanitization as a mandatory pattern rather than an optional convention. Every route, handler, executor, and utility that might throw an exception must catch the error and process it through either sanitizeErrorMessage directly or via buildErrorBody. The codebase enforces this through unit tests located in tests/unit/rule12-error-sanitization-sweep.test.ts, which scan source files to verify the correct import and call patterns exist.
The sanitization logic appears in the following key components:
| Component | Implementation File |
|---|---|
| API route handlers | src/app/api/v1/chat/completions/route.ts |
| SSE request handlers | open-sse/handlers/chatCore.ts |
| MCP server | open-sse/mcp-server/server.cjs |
| Authentication middleware | src/sse/services/auth.ts |
| Credential retry logic | src/sse/services/imageCredentialRetry.ts |
| General utility wrappers | src/shared/utils/fetchError.ts |
All imports resolve to the same open-sse/utils/error.ts implementation, ensuring no raw stack trace or file path can leak through any public endpoint, including Next.js API routes, SSE streaming layers, MCP servers, or embedded service endpoints.
Implementation Examples
The following patterns demonstrate how OmniRoute integrates error sanitization into different architectural layers.
Basic Sanitization in API Routes
import { sanitizeErrorMessage, buildErrorBody } from "@omniroute/open-sse/utils/error";
export async function GET(req: Request) {
try {
// …process request…
} catch (err) {
// The raw error might contain a stack trace and absolute paths.
const safe = sanitizeErrorMessage(err);
// Build a JSON payload that the client receives.
return new Response(buildErrorBody(safe), { status: 500 });
}
}
Error Handling in SSE Streams
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error";
export async function streamChat(req: Request) {
const encoder = new TextEncoder();
const stream = new ReadableStream({
async start(controller) {
try {
// …stream data…
} catch (err) {
const safeMsg = sanitizeErrorMessage(err);
controller.enqueue(encoder.encode(`event: error\ndata: ${safeMsg}\n\n`));
controller.close();
}
},
});
return new Response(stream, { headers: { "Content-Type": "text/event-stream" } });
}
Low-Level Utility Integration
import { sanitizeErrorMessage } from "@omniroute/open-sse/utils/error";
export async function fetchWithSafeError(url: string) {
try {
const res = await fetch(url);
if (!res.ok) throw new Error(`Upstream ${res.status}`);
return await res.json();
} catch (e) {
// Guarantees that no stack trace reaches the caller.
throw new Error(sanitizeErrorMessage(e));
}
}
Summary
- Centralized sanitization: All error handling flows through
open-sse/utils/error.ts, specifically thesanitizeErrorMessageandbuildErrorBodyfunctions. - Multi-layered protection: The system removes stack traces, replaces absolute paths with
<path>, and redacts credentials like tokens and API keys. - Universal enforcement: Hard Rule #12 applies to API routes, SSE handlers, MCP servers, and middleware, verified by automated unit tests in
tests/unit/rule12-error-sanitization-sweep.test.ts. - Input resilience: The sanitization handles arbitrary input types safely and includes ReDoS protection against maliciously crafted error messages.
Frequently Asked Questions
What constitutes error stack exposure and why is it dangerous?
Error stack exposure occurs when server-side applications return raw error messages, stack traces, or absolute file paths to clients. This information leakage reveals internal code structure, server file-system layouts, and potential vulnerabilities that attackers can exploit to craft targeted attacks against the application.
How does sanitizeErrorMessage handle non-string error inputs?
The sanitizeErrorMessage function normalizes all input types: undefined and null become empty strings, numbers convert to their string representation, and Error objects reduce to a safe "Error: <message>" format without their stack property. This ensures consistent, safe output regardless of what type of value was thrown or caught.
Where is OmniRoute's error sanitization logic centralized?
All error sanitization logic resides in open-sse/utils/error.ts within the diegosouzapw/OmniRoute repository. This file exports both sanitizeErrorMessage for cleaning raw error content and buildErrorBody for constructing standardized JSON error responses that are safe to transmit to clients.
How does OmniRoute ensure developers consistently use error sanitization?
OmniRoute enforces error sanitization through mandatory unit tests located in tests/unit/rule12-error-sanitization-sweep.test.ts. These tests programmatically scan the source codebase to verify that every error import and catch block correctly invokes sanitizeErrorMessage or buildErrorBody, preventing accidental deployment of routes that might leak sensitive error details.
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 →