swc-project/swc · error · TypeError

Symbol.dispose is not defined.

Error message

Symbol.dispose is not defined.

What it means

The sync path of `_ts_add_disposable_resource` requires `Symbol.dispose`. When the downleveled `using` declaration runs in a runtime without that well-known symbol, the helper throws `TypeError("Symbol.dispose is not defined.")` instead of silently skipping disposal.

Source

Thrown at crates/swc_ecma_transforms_base/src/helpers/generated/_ts_add_disposable_resource.rs:24

    name: HelperName::ts_add_disposable_resource,
    local: "_ts_add_disposable_resource",
    import_path: "@swc/helpers/_/_ts_add_disposable_resource",
    #[cfg(feature = "inline-helpers")]
    source: r#"function _ts_add_disposable_resource(env, value, async) {
    if (value !== null && value !== void 0) {
        if (typeof value !== "object" && typeof value !== "function") {
            throw new TypeError("Object expected.");
        }
        var dispose, inner;
        if (async) {
            if (!Symbol.asyncDispose) {
                throw new TypeError("Symbol.asyncDispose is not defined.");
            }
            dispose = value[Symbol.asyncDispose];
        }
        if (dispose === void 0) {
            if (!Symbol.dispose) {
                throw new TypeError("Symbol.dispose is not defined.");
            }
            dispose = value[Symbol.dispose];
            if (async) {
                inner = dispose;
            }
        }
        if (typeof dispose !== "function") {
            throw new TypeError("Object not disposable.");
        }
        if (inner) {
            dispose = function() {
                try {
                    inner.call(this);
                } catch (e) {
                    return Promise.reject(e);
                }
            };
        }

View on GitHub (pinned to 5176682b65)

Solutions

  1. Polyfill once at startup: `Symbol.dispose ??= Symbol('Symbol.dispose');` and ensure resources define `[Symbol.dispose]` under that key.
  2. Use a polyfill package for explicit resource management that installs Symbol.dispose and DisposableStack.
  3. Upgrade the runtime to one shipping Symbol.dispose.
  4. Fall back to `try/finally` with manual dispose calls on legacy runtimes.

Example fix

// before
using conn = openConnection(); // Node 18: TypeError: Symbol.dispose is not defined.

// after
Symbol.dispose ??= Symbol('Symbol.dispose');
using conn = openConnection();
Defensive patterns

Strategy: fallback

Validate before calling

// Feature-detect and polyfill Symbol.dispose at the entry point.
if (typeof Symbol.dispose === 'undefined') {
  Symbol.dispose = Symbol('Symbol.dispose');
  // Attach resources under the same key: res[Symbol.dispose] ??= () => res.close();
}

Type guard

const supportsDispose = (): boolean => typeof (Symbol as { dispose?: symbol }).dispose === 'symbol';

Try / catch

try {
  using res = open();
} catch (err) {
  if (err instanceof TypeError && /Symbol.dispose is not defined/.test(err.message)) {
    const res = open();
    try { /* work */ } finally { res.close(); } // manual disposal fallback
  } else throw err;
}

Prevention

When it happens

Trigger: `using x = res;` executed where `Symbol.dispose` is absent (Node < 20, ES2022-era browsers) with output compiled to the TS helper.

Common situations: Same as the async case: modern TS features deployed to older LTS runtimes; edge/serverless runtimes pinned to old V8; assuming the helper brings its own symbol.

Related errors


AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17). Data as JSON: /api/errors/c4880bea2466ca1e. Report an issue: GitHub.