From 39998dcbb2a610d0325eadf01cee0b6396e681c9 Mon Sep 17 00:00:00 2001 From: nickwinder Date: Wed, 13 May 2026 11:43:04 +1200 Subject: [PATCH] fix(eval): swallow microsandbox 'egress frame too large' so the eval doesn't die mid-run MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit microsandbox's egressIntercept() runs its event-pump loop as a fire-and-forget async IIFE with try/finally — no catch. When the native binding throws "egress frame too large" (the protocol parser hit a frame above its internal buffer), the rejection escapes as unhandledRejection and crashes the parent node process, taking out the whole eval. We've reproduced this consistently on TC-005 with the Nutrient Web SDK suite — TC-001 finishes, then the next test's executor sandbox triggers the bug and the entire eval dies before any judging happens. Install a process-level unhandledRejection handler at the CLI entry point that ONLY catches errors with code === 'GenericFailure' AND message matching /egress frame too large/. Re-throw everything else so genuine unhandled rejections still crash loudly. Egress logging for that one test case stops (it's observability, not correctness — the eval can still execute, snapshot, and judge). After this fix the same TC-001+TC-005 run completes and both produce judge results. --- src/index.ts | 18 ++++++++++++++++++ 1 file changed, 18 insertions(+) diff --git a/src/index.ts b/src/index.ts index 5d8ef74..90f93a2 100644 --- a/src/index.ts +++ b/src/index.ts @@ -1,5 +1,23 @@ #!/usr/bin/env node +// microsandbox's egress-intercept background loop runs as a fire-and-forget IIFE +// inside its egressIntercept() helper, with try/finally but no catch. When the +// native binding throws (most commonly "egress frame too large" — a frame from +// the sandbox exceeds the protocol parser's buffer), it becomes an +// unhandledRejection and crashes the parent process mid-run, taking out the +// whole eval. Swallow that specific error so the eval keeps making forward +// progress; egress logging for that test case stops, which is acceptable +// (egress logs are observability, not correctness). Re-throw everything else. +process.on('unhandledRejection', (err) => { + const e = err as { message?: string; code?: string } | null | undefined; + const msg = e?.message ?? ''; + if (e?.code === 'GenericFailure' && /egress frame too large/i.test(msg)) { + console.warn(`\n[egress] interception loop terminated mid-run: ${msg} — eval continuing without further egress logging for this case`); + return; + } + throw err as Error; +}); + import { Command } from 'commander'; import chalk from 'chalk'; import { readFileSync } from 'node:fs';