swc-project/swc · error · TypeError

Object not disposable.

Error message

Object not disposable.

What it means

After resolving the disposal method from the resource (`value[Symbol.dispose]`, or `value[Symbol.asyncDispose]` for `await using`), `_ts_add_disposable_resource` verifies it is callable. If the resource is an object/function but the resolved method is missing or not a function, it throws `TypeError("Object not disposable.")`.

Source

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

        }
        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);
                }
            };
        }
        env.stack.push({ value: value, dispose: dispose, async: async });
    } else if (async) {
        env.stack.push({ async: true });
    }
    return value;
}
"#,
    #[cfg(feature = "inline-helpers")]

View on GitHub (pinned to 5176682b65)

Solutions

  1. Implement the symbol-keyed method: `class Conn { [Symbol.dispose]() { this.close(); } }`.
  2. If wrapping third-party objects, build a disposable adapter: `{ [Symbol.dispose]: () => obj.close() }`.
  3. Check `typeof res?.[Symbol.dispose] === 'function'` before the `using` statement.

Example fix

// before
class TempDir { dispose() { rmSync(this.path); } }
using tmp = new TempDir(); // TypeError: Object not disposable.

// after
class TempDir { [Symbol.dispose]() { rmSync(this.path); } }
using tmp = new TempDir();
Defensive patterns

Strategy: type-guard

Validate before calling

// Check disposability before the `using` statement.
const isDisposable = (v, async = false) => {
  if (v == null) return true;
  if (typeof v !== 'object' && typeof v !== 'function') return false;
  const m = v[async ? Symbol.asyncDispose : Symbol.dispose];
  return typeof m === 'function';
};
const res = open();
if (!isDisposable(res)) res[Symbol.dispose] = () => res.close();

Type guard

const isDisposable = (v: unknown): v is Disposable =>
  (typeof v === 'object' || typeof v === 'function') &&
  v !== null &&
  typeof (v as Disposable)[Symbol.dispose] === 'function';

Prevention

When it happens

Trigger: `using x = {};` or `using x = { [Symbol.dispose]: null };` — any non-null object or function whose `[Symbol.dispose]` (or async variant) is absent or non-callable.

Common situations: Forgetting to implement `[Symbol.dispose]` on a connection/wrapper class; using a string-keyed `dispose()` method instead of the symbol key; a mock/stub resource in tests that lacks the symbol method.

Related errors


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