Skip to content

JS_FreeRuntime assertion fails after OOM with persistent objects #257

@bnimit

Description

@bnimit

JS_FreeRuntime assertion fails after OOM with persistent objects

Version: quickjs-emscripten@0.32.0

Description

Calling ctx.dispose() after an OOM exception crashes the process with a WASM abort when the VM has any persistent heap objects (arrays, plain objects, etc.).

Assertion failed: list_empty(&rt->gc_obj_list), at: ../../vendor/quickjs/quickjs.c,2036,JS_FreeRuntime

This regression was introduced in 0.32.0. The same code works correctly in 0.31.0.

Minimal Reproduction

import { getQuickJS } from "quickjs-emscripten";

const ctx = (await getQuickJS()).newContext();
ctx.runtime.setMemoryLimit(200 * 1024);

// Any persistent VM object triggers the issue
ctx.evalCode(`var arr = [];`);

// Trigger OOM
try {
  ctx.evalCode(`for (let i = 0; i < 1e6; i++) { arr.push("x".repeat(100)); }`);
} catch (e) {}

ctx.runtime.setMemoryLimit(-1); // Removing the limit does not help
ctx.dispose(); // 💥 Aborted: Assertion failed: list_empty(&rt->gc_obj_list)

### Expected Behavior 
ctx.dispose() completes cleanly after an OOM exception is caught.

Actual Behavior

WASM Abort:

Assertion failed: list_empty(&rt->gc_obj_list), at: ../../vendor/quickjs/quickjs.c,2036,JS_FreeRuntime

Notes

  • Works correctly in quickjs-emscripten@0.31.0
  • The assertion list_empty(&rt->gc_obj_list) was added to JS_FreeRuntime in QuickJS 2025-09-13
  • OOM appears to leave some objects in an inconsistent state that prevents JS_FreeContext's cascade-free from completing before the runtime is torn down
  • Removing the memory limit before ctx.dispose() does not resolve the crash
  • Plain OOM with only transient (stack-local) objects does not trigger the crash — a persistent global variable is required to reproduce

Environment

  • quickjs-emscripten: 0.32.0
  • Node.js: v25.8.0
  • Platform: macOS (darwin)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions