Files
OmniRoute/tests/unit/stream-handler-catch-logging-8143.test.ts
Chirag Singhal 8565954e65 fix(stream): add logging to empty catch blocks in stream error handling (#8143)
* fix(combo,model-fallback,sqljs): three stream-reliability fixes

- targetExhaustion: skip remaining same-provider models on 401 auth failure
  (prevents opencode-zen noauth cascade wasting retry attempts) (#8133)
- modelFamilyFallback: skip unsupported models in T5 fallback chain
  (prevents GitHub provider trying deprecated claude-opus-4.8/4.7) (#8134)
- sqljsAdapter: split package.json resolve string to suppress Next.js
  Can't resolve warning at build time (#8135)

* fix(stream): add logging to empty catch blocks in stream error handling

- stream.ts: Log errors in onComplete/onFailure callbacks (lines 929, 1112, 2451, 2536, 2561, 2717)
- streamHandler.ts: Log errors in stall watchdog and trackPendingRequest (lines 249, 334, 657, 663, 667)
- cursor.ts: Add comments to intentional H2 lifecycle catches, log KV/exec errors
- next.config.mjs: Externalize sql.js to suppress build warnings

Closes #8138, #8139, #8140, #8141, #8142

* refactor(stream): scope PR to logging hygiene, drop out-of-scope exhaustion/fallback hunks

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

* chore(quality): rebaseline stream.ts for #8143 empty-catch logging own-growth

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>

---------

Co-authored-by: diegosouzapw <8016841+diegosouzapw@users.noreply.github.com>
Co-authored-by: rafaumeu <rafael.zendron22@gmail.com>
Co-authored-by: chirag127 <chirag127@users.noreply.github.com>
2026-07-23 04:50:51 -03:00

70 lines
2.0 KiB
TypeScript

import test from "node:test";
import assert from "node:assert/strict";
import { pipeWithDisconnect } from "../../open-sse/utils/streamHandler.ts";
const encoder = new TextEncoder();
const decoder = new TextDecoder();
async function readStreamText(stream) {
const reader = stream.getReader();
const chunks = [];
while (true) {
const { done, value } = await reader.read();
if (done) break;
chunks.push(value);
}
return decoder.decode(
chunks.length === 1 ? chunks[0] : Uint8Array.from(chunks.flatMap((chunk) => Array.from(chunk)))
);
}
// Regression guard for #8143's logging-hygiene fix: the stall watchdog's
// `streamController.handleError?.()` call used to be wrapped in a bare
// `catch {}` that silently swallowed any exception raised by the callback.
// It must now log via console.debug so the failure is observable instead of
// vanishing without a trace.
test("pipeWithDisconnect stall watchdog logs instead of silently swallowing a throwing handleError", async () => {
const source = new ReadableStream({
start(controller) {
controller.enqueue(encoder.encode("x"));
// never enqueue again, never close — forces the stall watchdog to fire
},
cancel() {},
});
const streamController = {
isConnected: () => true,
handleError() {
throw new Error("handleError callback exploded");
},
handleComplete() {},
abort() {},
};
const debugCalls = [];
const originalDebug = console.debug;
console.debug = (...args) => {
debugCalls.push(args);
};
try {
const stream = pipeWithDisconnect(new Response(source), new TransformStream(), streamController, {
stallTimeoutMs: 40,
});
await readStreamText(stream);
} finally {
console.debug = originalDebug;
}
const loggedStallFailure = debugCalls.some((args) =>
String(args[0]).includes("stall watchdog handleError failed")
);
assert.ok(
loggedStallFailure,
"a throwing handleError during the stall watchdog must be logged via console.debug, not swallowed"
);
});