fix: surface app errors in gateway logs

This commit is contained in:
2026-07-20 14:26:27 +05:30
parent aa2595ec01
commit 2b4083c6db
56 changed files with 147 additions and 86 deletions
+30 -1
View File
@@ -128,6 +128,27 @@ function timingSafeEqual(a: string, b: string): boolean {
return diff === 0;
}
export function internalError(res: Response): string | null {
const encoded = res.headers.get("x-wrnexus-internal-error");
if (!encoded) return null;
try {
return decodeURIComponent(encoded);
} catch {
return "Malformed internal error diagnostic";
}
}
export function stripInternalError(res: Response): Response {
if (!res.headers.has("x-wrnexus-internal-error")) return res;
const headers = new Headers(res.headers);
headers.delete("x-wrnexus-internal-error");
return new Response(res.body, {
status: res.status,
statusText: res.statusText,
headers,
});
}
/** Preserve an intentional verifier redirect while keeping other failures opaque. */
export function forwardAuthFailure(res: Response, verifierUrl: string): Response {
const location = res.headers.get("location");
@@ -210,8 +231,9 @@ async function checkAuth(
if (!res.ok) {
if (res.status >= 500) {
const requestUrl = new URL(req.url);
const diagnostic = internalError(res);
console.error(
`[wrnexus] forward-auth verifier error: ${req.method} ${requestUrl.pathname} via ${verifyUrl} returned ${res.status}`,
`[wrnexus] forward-auth verifier error: ${req.method} ${requestUrl.pathname} via ${verifyUrl} returned ${res.status}${diagnostic ? `${diagnostic}` : ""}`,
);
}
return forwardAuthFailure(res, verifyUrl);
@@ -506,6 +528,13 @@ export async function startGateway(opts: GatewayOptions): Promise<RunningGateway
);
res = new Response(`Gateway: app '${target.name}' is unavailable.`, { status: 502 });
}
const diagnostic = internalError(res);
if (res.status >= 500 && diagnostic) {
console.error(
`[wrnexus] app response error: ${target.name} ${req.method} ${url.pathname}${res.status}${diagnostic}`,
);
}
res = stripInternalError(res);
if (sec.headers) res = applyEdgeHeaders(res);
if (sec.accessLog)
console.log(
+7 -1
View File
@@ -791,10 +791,16 @@ export function createHandlers(deps: RuntimeDeps): Handlers {
const app = process.env.WRNEXUS_APP_NAME ?? "app";
const detail =
err instanceof Error ? (err.stack ?? `${err.name}: ${err.message}`) : String(err);
const message = err instanceof Error ? `${err.name}: ${err.message}` : String(err);
console.error(
`[wrnexus] unhandled request error (${app}) ${req.method} ${url.pathname}\n${detail}`,
);
return compressResponse(req, secure(renderError(err, mode)));
const response = secure(renderError(err, mode));
// Gateway-managed production apps bind to loopback. Carry a bounded,
// encoded diagnostic to the parent gateway so centralized log collectors
// can explain child failures; the gateway always strips this header.
response.headers.set("x-wrnexus-internal-error", encodeURIComponent(message.slice(0, 500)));
return compressResponse(req, response);
}
}