Skip to content

Commit 6ab6d0c

Browse files
committed
fix(@probitas/probitas): suppress node:http2 BadResource errors in subprocess
Add unhandledrejection handler to subprocess run template, mirroring the error suppression strategy used in the previous Worker-based execution. The "Bad resource ID" errors from node:http2 occur during HTTP/2 stream cleanup when the subprocess exits. These errors don't affect test correctness but caused subprocess crashes. The previous commit (d820216) improved resource cleanup by awaiting writer.close() and reordering parent-subprocess cleanup. However, the 100ms grace period was a workaround. This commit replaces the timeout approach with proper error suppression, matching Worker behavior: - Add globalThis unhandledrejection listener in run.ts - Silently ignore BadResource errors from node:http2 - Log other unhandled rejections for debugging - Remove 100ms grace period (revert to 0ms) This allows HTTP/2 cleanup errors to be safely ignored while preserving error visibility for legitimate issues.
1 parent d820216 commit 6ab6d0c

2 files changed

Lines changed: 28 additions & 6 deletions

File tree

src/cli/_templates/list.ts

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -84,6 +84,6 @@ async function main(): Promise<void> {
8484

8585
// Run main and exit explicitly to avoid async operations keeping process alive
8686
main().finally(() => {
87-
// Give resources time to clean up properly
88-
setTimeout(() => Deno.exit(0), 100);
87+
// Ensure process exits after output is flushed
88+
setTimeout(() => Deno.exit(0), 0);
8989
});

src/cli/_templates/run.ts

Lines changed: 26 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -34,6 +34,30 @@ const logger = getLogger(["probitas", "cli", "run", "subprocess"]);
3434
// Global AbortController for graceful shutdown
3535
let globalAbortController: AbortController | null = null;
3636

37+
// Handle unhandled promise rejections from Deno's node:http2 compatibility layer.
38+
// The "Bad resource ID" error occurs during HTTP/2 stream cleanup and doesn't
39+
// affect test correctness, but causes the subprocess to crash without this handler.
40+
globalThis.addEventListener(
41+
"unhandledrejection",
42+
(event: PromiseRejectionEvent) => {
43+
event.preventDefault();
44+
45+
const error = event.reason;
46+
const errorMessage = error instanceof Error ? error.message : String(error);
47+
48+
// Silently ignore "Bad resource ID" errors from node:http2
49+
if (
50+
errorMessage.includes("Bad resource ID") ||
51+
(error instanceof Error && error.stack?.includes("node:http2"))
52+
) {
53+
return;
54+
}
55+
56+
// Log other unhandled rejections
57+
logger.error`Unhandled promise rejection in subprocess: ${error}`;
58+
},
59+
);
60+
3761
/**
3862
* Execute all scenarios
3963
*/
@@ -156,8 +180,6 @@ async function main(): Promise<void> {
156180

157181
// Run main and exit explicitly to avoid LogTape keeping process alive
158182
main().finally(() => {
159-
// Give HTTP/2 and other resources time to clean up properly.
160-
// This prevents "BadResource: Bad resource ID" errors from node:http2
161-
// that occur when connections are closed while HTTP/2 streams are active.
162-
setTimeout(() => Deno.exit(0), 100);
183+
// Ensure process exits after output is flushed
184+
setTimeout(() => Deno.exit(0), 0);
163185
});

0 commit comments

Comments
 (0)