fix(code-runtime-python): meter the exception diagnostic by serialized cost

Raising maxValueBytes' load bound to ceiling-envelope assumed both budgets are
metered in serialized (JSON-escaped) bytes, which held for completion values and
logs but not the diagnostic: _cap_message capped by raw UTF-8, so a control-heavy
message near maxValueBytes could serialize sixfold and breach the fd-3 frame
ceiling — the silent worker-exit inversion the load check prevents. _cap_message
now accumulates per-byte serialized cost (new _JSON_BYTE_COST table) and cuts the
prefix that fits. Also reword the host SIGXCPU timeout message to name cpuSeconds
as the configured ceiling rather than a budget a stricter inherited RLIMIT_CPU
soft may undercut. Adds a control-heavy-diagnostic regression test.
This commit is contained in:
Chinesezjc
2026-08-31 14:21:57 +08:00
committed by Tianyi Cui
parent e0d5d8d097
commit 63c49c8a90
3 changed files with 79 additions and 20 deletions
@@ -1162,8 +1162,15 @@ export class PythonCodeRuntime extends CodeRuntime {
// OOM killer, an operator, or itself consumed none), so every other
// signal or code — including an unsolicited SIGKILL, even the
// hard-limit one — reports as an opaque worker exit.
//
// The message names `cpuSeconds` as the CONFIGURED ceiling, not "the
// budget that fired": the child clamps RLIMIT_CPU to the stricter of
// `cpuSeconds` and any inherited soft limit, so under a tighter inherited
// cap SIGXCPU arrives before `cpuSeconds` — the host cannot see the
// effective value, so it states the ceiling it set rather than a second
// count it cannot guarantee.
finish(signal === 'SIGXCPU'
? { error: { kind: 'timeout', message: `CPU budget (${this.config.cpuSeconds}s) exhausted` } }
? { error: { kind: 'timeout', message: `CPU time exhausted (limit at most the configured ${this.config.cpuSeconds}s; a stricter inherited RLIMIT_CPU can fire sooner)` } }
: { error: { kind: 'worker-exit', message: `python exited (code=${String(code)}, signal=${String(signal)}) before completing` } })
settle(decided)
})