swc-project/swc · error · TypeError
Property [Symbol.dispose] is not a function.
Error message
Property [Symbol.dispose] is not a function.
What it means
Runtime TypeError from the same _using_ctx helper SWC emits for `using` / `await using` lowering. After the value passes the object check, the helper resolves the dispose method — `Symbol.asyncDispose` for `await using`, falling back to `Symbol.dispose` — and requires it to be a function. If the resource has no such well-known method (or a non-function property under that key), the helper throws at the moment the using declaration executes.
Source
Thrown at crates/swc_ecma_transforms_base/src/helpers/mod.rs:633
var err = new Error();
err.name = "SuppressedError";
err.suppressed = suppressed;
err.error = error;
return err;
}, empty = {}, stack = [];
function using(isAwait, value) {
if (value != null) {
if (Object(value) !== value) {
throw new TypeError("using declarations can only be used with objects, functions, null, or undefined.");
}
if (isAwait) {
var dispose = value[Symbol.asyncDispose || Symbol.for("Symbol.asyncDispose")];
}
if (dispose == null) {
dispose = value[Symbol.dispose || Symbol.for("Symbol.dispose")];
}
if (typeof dispose !== "function") {
throw new TypeError(`Property [Symbol.dispose] is not a function.`);
}
stack.push({
v: value,
d: dispose,
a: isAwait
});
} else if (isAwait) {
stack.push({
d: value,
a: isAwait
});
}
return value;
}
return {
e: empty,
u: using.bind(null, false),
a: using.bind(null, true),View on GitHub (pinned to 5176682b65)
Solutions
- Implement `[Symbol.dispose]()` on the resource class, or `[Symbol.asyncDispose]()` for async-only resources used with `await using`
- Adapt legacy resources at the binding site: `using wrapped = { [Symbol.dispose]: () => handle.close() }` (or use TypeScript's DisposableStack)
- Remember `await using` accepts a sync [Symbol.dispose] as fallback, but a resource with only async cleanup must expose [Symbol.asyncDispose]
- If registering via Symbol.for('Symbol.dispose'), prefer the native Symbol.dispose key when the runtime defines it, so the helper's lookup finds it
Example fix
// before
using handle = getHandle(); // { destroy(): void } -> Property [Symbol.dispose] is not a function.
// after
const handle = getHandle();
using wrapper = { [Symbol.dispose]: () => handle.destroy() }; Defensive patterns
Strategy: type-guard
Validate before calling
function assertDisposable(value, { isAwait = false } = {}) {
if (value == null) return; // null/undefined are legal no-ops
if (Object(value) !== value) return; // handled by the primitive check
const method = isAwait
? value[Symbol.asyncDispose] ?? value[Symbol.dispose]
: value[Symbol.dispose];
if (typeof method !== 'function') {
throw new TypeError('Resource bound with `using` has no callable [Symbol.dispose].');
}
} Type guard
interface Disposable { [Symbol.dispose](): void; }
interface AsyncDisposable { [Symbol.asyncDispose](): Promise<void>; }
function isDisposable(v: unknown): v is Disposable {
return v != null && (typeof v === 'object' || typeof v === 'function')
&& typeof (v as Disposable)[Symbol.dispose] === 'function';
}
function isAsyncDisposable(v: unknown): v is AsyncDisposable {
return v != null && (typeof v === 'object' || typeof v === 'function')
&& typeof (v as AsyncDisposable)[Symbol.asyncDispose] === 'function';
} Try / catch
try {
using res = acquire();
} catch (e) {
if (e instanceof TypeError && /Symbol\.dispose\] is not a function/.test(e.message)) {
// resource lacks a dispose method — wrap it before binding, do not retry
}
throw e;
} Prevention
- Model resources as TS 5.2+ Disposable/AsyncDisposable so the compiler enforces the well-known method
- Centralize legacy cleanup adaptation in a wrapDisposable(handle) utility instead of per-callsite objects
- For `await using`, verify asyncDispose exists rather than assuming sync dispose is enough
When it happens
Trigger: `using res = { close() {} }` — cleanup named close/destroy/release/end instead of [Symbol.dispose]; `await using x = obj` where obj defines [Symbol.dispose] as a non-function value; a resource registered under `Symbol.for('Symbol.dispose')` while the runtime has the native Symbol.dispose (the helper's lookup key `Symbol.dispose || Symbol.for(...)` then misses the registration).
Common situations: Migrating try/finally DB/stream cleanup to `using` without adapting legacy cleanup APIs; third-party handles exposing .destroy() (sockets) or .release() (locks); polyfill environments where well-known symbol identity differs from Symbol.for registrations.
Related errors
- using declarations can only be used with objects, functions,
- Symbol.dispose is not defined.
- Object not disposable.
- Property [Symbol.dispose] is not a function.
- using declaration must be removed by previous pass
AI-assisted analysis of swc-project/swc@5176682b65 (2026-08-17).
Data as JSON: /api/errors/34646b54af03fa89.
Report an issue: GitHub.