microsoft/TypeScript · error · TypeError
Object not disposable.
Error message
Object not disposable.
What it means
Runtime TypeError from `__addDisposableResource`. After the symbol checks, the helper reads the disposer (`value[Symbol.asyncDispose]`/`value[Symbol.dispose]`) and throws if it is not a function. I.e. the value is an object but does not actually implement the disposable protocol for the chosen mode.
Source
Thrown at src/compiler/factory/emitHelpers.ts:1398
const addDisposableResourceHelper: UnscopedEmitHelper = {
name: "typescript:addDisposableResource",
importName: "__addDisposableResource",
scoped: false,
text: `
var __addDisposableResource = (this && this.__addDisposableResource) || function (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); } };
env.stack.push({ value: value, dispose: dispose, async: async });
}
else if (async) {
env.stack.push({ async: true });
}
return value;
};`,
};
/**
* The `s` variable represents two boolean flags from the `DisposeResources` algorithm:
* - `needsAwait` (`1`) — Indicates that an `await using` for a `null` or `undefined` resource was encountered.
* - `hasAwaited` (`2`) — Indicates that the algorithm has performed an Await.
*/
const disposeResourcesHelper: UnscopedEmitHelper = {
name: "typescript:disposeResources",
importName: "__disposeResources",
View on GitHub (pinned to b465fdbfe1)
Solutions
- Implement `[Symbol.dispose]()` (sync) or `[Symbol.asyncDispose]()` (async) on the object.
- Adapt an existing API with a small wrapper: `{ [Symbol.dispose]() { resource.close(); } }`.
- Type the binding as `Disposable`/`AsyncDisposable` so non-disposable objects are rejected at compile time.
- Use `null`/`undefined` for the binding when there is nothing to clean up (helper skips it).
Example fix
// before
using conn = { url: "..." }; // no [Symbol.dispose] -> TypeError
// after
using conn = {
url: "...",
[Symbol.dispose]() { /* close socket */ },
}; Defensive patterns
Strategy: validation
Validate before calling
function ensureDisposable<T>(v: T): T extends Disposable ? T : never {
if (v !== null && v !== undefined && typeof (v as any)[Symbol.dispose] !== "function") {
throw new TypeError("Object not disposable");
}
return v as any;
} Type guard
function isDisposable(v: unknown): v is Disposable {
return v !== null && v !== undefined && typeof (v as any)[Symbol.dispose] === "function";
} Prevention
- Type `using`/`await using` bindings as `Disposable`/`AsyncDisposable`.
- Wrap resources that expose close/dispose methods instead of binding the raw object.
- Bind `null`/`undefined` when acquisition failed and there is nothing to release.
When it happens
Trigger: `using x = obj` where `obj` has no `[Symbol.dispose]`, or `await using x = obj` where `obj` has neither `[Symbol.asyncDispose]` nor `[Symbol.dispose]`. Reached after the symbol-existence checks pass, so the runtime supports disposal but the object doesn't participate.
Common situations: Passing a plain config/record object to `using`; third-party resources that expose `close()`/`dispose()` methods but not the well-known symbol; partial wrappers that implement one mode but are used with the other.
Related errors
- Object expected.
- Symbol.asyncDispose is not defined.
- Symbol.dispose is not defined.
- Function expected
- Cannot add initializers after decoration has completed
AI-assisted analysis of microsoft/TypeScript@b465fdbfe1 (2026-08-12).
Data as JSON: /api/errors/a667120c0fddbf7a.
Report an issue: GitHub.