fix(editor): fatal overlay matches Firefox trap messages + taps pthread-worker errors
The v0.1.19 prod crash (console-export-2026-7-31_17-49-20.log) opened with Firefox's bare "index out of bounds" — no "RuntimeError" prefix, no "table" — which the terminal-signature regex only knew in Chrome's spelling, so the overlay this feature exists for never promoted on the very trap it was built against. Match the bare form (+ "null function or function signature", the other Firefox spelling in this family). Also wrap the Worker constructor attach-only: a pthread worker's uncaught error fires an ErrorEvent on the Worker OBJECT, never on window, so worker crashes (raytracer pool etc.) were invisible to both listeners. The editor's main()/wx run on the page thread (no PROXY_TO_PTHREAD on the link line) — the tap is defense-in-depth, not the primary fix. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019SE4o46Lnq3hF574FFq8x4
This commit is contained in:
parent
579f559b6c
commit
197f317bfb
1 changed files with 31 additions and 1 deletions
|
|
@ -1185,8 +1185,12 @@ export function WasmTool({
|
|||
// genuinely terminal signatures promote to the fatal overlay; ordinary app
|
||||
// errors must not hijack a working editor.
|
||||
React.useEffect(() => {
|
||||
// NOTE: matched against `e.error.message`, which is BARE — Firefox's first
|
||||
// trap is literally "index out of bounds" (no "RuntimeError", no "table")
|
||||
// and slipped through the original pattern; the v0.1.19 prod log opens with
|
||||
// exactly that message.
|
||||
const terminal = (msg: string) =>
|
||||
/RuntimeError|\babort(ed)?\b|table index is out of bounds|indirect call signature|memory access out of bounds|unreachable executed/i.test(
|
||||
/RuntimeError|\babort(ed)?\b|\bindex out of bounds|indirect call signature|memory access out of bounds|unreachable executed|null function or function signature/i.test(
|
||||
msg,
|
||||
);
|
||||
const onError = (e: ErrorEvent) => {
|
||||
|
|
@ -1203,9 +1207,35 @@ export function WasmTool({
|
|||
append(dumpTrace());
|
||||
setFatal(msg);
|
||||
};
|
||||
// With PROXY_TO_PTHREAD, main()/wx/timers — and therefore every asyncify
|
||||
// trap in this family — throw INSIDE a pthread worker. A worker's uncaught
|
||||
// error fires an ErrorEvent on the Worker OBJECT, never on `window`, so the
|
||||
// two listeners below can't see the very traps this overlay exists for
|
||||
// (v0.1.19 prod: three "Uncaught RuntimeError"s, overlay never promoted).
|
||||
// Wrap the constructor attach-only: the glue spawns all pthread workers
|
||||
// from this realm, so every one gets an error tap.
|
||||
const NativeWorker = window.Worker;
|
||||
const onWorkerError = (e: ErrorEvent) => {
|
||||
const msg = String(e.message ?? "");
|
||||
if (!terminal(msg)) return;
|
||||
append(`[fatal] worker error: ${msg}`);
|
||||
append(dumpTrace());
|
||||
setFatal(msg);
|
||||
};
|
||||
const PatchedWorker = function (
|
||||
this: unknown,
|
||||
...args: ConstructorParameters<typeof Worker>
|
||||
) {
|
||||
const w = new NativeWorker(...args);
|
||||
w.addEventListener("error", onWorkerError);
|
||||
return w;
|
||||
} as unknown as typeof Worker;
|
||||
PatchedWorker.prototype = NativeWorker.prototype;
|
||||
window.Worker = PatchedWorker;
|
||||
window.addEventListener("error", onError);
|
||||
window.addEventListener("unhandledrejection", onRejection);
|
||||
return () => {
|
||||
window.Worker = NativeWorker;
|
||||
window.removeEventListener("error", onError);
|
||||
window.removeEventListener("unhandledrejection", onRejection);
|
||||
};
|
||||
|
|
|
|||
Loading…
Reference in a new issue