Pyodide 'Error in sys.excepthook: KeyboardInterrupt' when two interrupt-buffer writes land back-to-back
Pyodide 0.27.7 in a Web Worker with setInterruptBuffer(SharedArrayBuffer). Sentry occasionally reports PythonError: Error in sys.excepthook: Traceback ... KeyboardInterrupt with PythonError.type === 'KeyboardInterrupt' instead of the plain KeyboardInterrupt traceback that a normal cancel produces. The main thread writes 2 to the buffer from a Cancel button and from a 10s watchdog setTimeout. I assumed the user double-clicked Cancel, but the events also came from sessions where no cancel happened. Could not reproduce in Node by writing the buffer once; Pyodide's signal-check helper reads the value and resets it to 0, so a single write only ever trips one SIGINT.
Two 2 writes to the interrupt buffer within the same JS turn (or a few ms apart) trigger two separate KeyboardInterrupts in Python, and the second one lands while Pyodide is still converting the first exception to a JS PythonError (it runs traceback.format_exception in Python to build the message). That nested raise produces the Error in sys.excepthook: wrapper, still with type === 'KeyboardInterrupt'.
In my case the double write came from the main thread dispatching the same RPC twice (a Svelte writable.update() synchronously invoked a store subscriber that re-entered the dispatch function before its running flag was set), so two 10s watchdog timers fired in the same tick and each wrote interrupt_buffer[0] = 2.
Fixes:
- Make sure only one writer can set the buffer per in-flight call (one RPC per run; make
interrupt()a no-op unless a run is actually in flight). - In the worker, classify both variants by
err instanceof pyodide.ffi.PythonError && err.type === 'KeyboardInterrupt'; themessagediffers between the two forms,typedoes not. - Reset the buffer to 0 at the start of each run in the worker (
interrupt_buffer[0] = 0).
Verified in Node: writing 2 once from a Python-called JS callback inside a while True: pass raises exactly one KeyboardInterrupt; subsequent calls run normally, so stale signals are not the cause.