microsoft/TypeScript · error · TypeError
Cannot add initializers after decoration has completed
Error message
Cannot add initializers after decoration has completed
What it means
In `__esDecorate`, each decorator's context.addInitializer pushes into an extraInitializers array; once the decoration loop completes the helper sets `done = true`. Calling addInitializer afterwards throws, matching the spec rule that initializers must be registered synchronously during decoration.
Source
Thrown at src/compiler/factory/emitHelpers.ts:783
// ES Decorators Helpers
const esDecorateHelper: UnscopedEmitHelper = {
name: "typescript:esDecorate",
importName: "__esDecorate",
scoped: false,
priority: 2,
text: `
var __esDecorate = (this && this.__esDecorate) || function (ctor, descriptorIn, decorators, contextIn, initializers, extraInitializers) {
function accept(f) { if (f !== void 0 && typeof f !== "function") throw new TypeError("Function expected"); return f; }
var kind = contextIn.kind, key = kind === "getter" ? "get" : kind === "setter" ? "set" : "value";
var target = !descriptorIn && ctor ? contextIn["static"] ? ctor : ctor.prototype : null;
var descriptor = descriptorIn || (target ? Object.getOwnPropertyDescriptor(target, contextIn.name) : {});
var _, done = false;
for (var i = decorators.length - 1; i >= 0; i--) {
var context = {};
for (var p in contextIn) context[p] = p === "access" ? {} : contextIn[p];
for (var p in contextIn.access) context.access[p] = contextIn.access[p];
context.addInitializer = function (f) { if (done) throw new TypeError("Cannot add initializers after decoration has completed"); extraInitializers.push(accept(f || null)); };
var result = (0, decorators[i])(kind === "accessor" ? { get: descriptor.get, set: descriptor.set } : descriptor[key], context);
if (kind === "accessor") {
if (result === void 0) continue;
if (result === null || typeof result !== "object") throw new TypeError("Object expected");
if (_ = accept(result.get)) descriptor.get = _;
if (_ = accept(result.set)) descriptor.set = _;
if (_ = accept(result.init)) initializers.unshift(_);
}
else if (_ = accept(result)) {
if (kind === "field") initializers.unshift(_);
else descriptor[key] = _;
}
}
if (target) Object.defineProperty(target, contextIn.name, descriptor);
done = true;
};`,
};
View on GitHub (pinned to b465fdbfe1)
Solutions
- Call context.addInitializer synchronously inside the decorator body.
- Do not store or capture the addInitializer function for deferred use.
- If you need lazy behavior, register a single function that does the deferred work when invoked.
Example fix
// before
function deco(v, ctx) {
setTimeout(() => ctx.addInitializer(() => console.log("hi")), 0); // throws after done
}
// after
function deco(v, ctx) {
ctx.addInitializer(() => console.log("hi")); // register synchronously
} Defensive patterns
Strategy: validation
Validate before calling
// Register initializers synchronously during decoration only.
function deco(value: unknown, ctx: ClassMemberDecoratorContext) {
ctx.addInitializer(() => { /* ... */ }); // OK: synchronous, inside decorator
} Prevention
- Never capture or defer context.addInitializer; call it inside the decorator body.
- If deferred work is needed, register a single function that performs it when invoked.
When it happens
Trigger: A decorator captures context.addInitializer and invokes it after the decoration loop has finished — e.g. from a later microtask, an async callback, or from within a deferred initializer.
Common situations: Decorator that defers registration to setTimeout/Promise resolution; storing addInitializer for reuse; re-entering it from another decorator's later phase.
Related errors
- Function expected
- Object expected
- Symbol.asyncIterator is not defined.
- Class extends value ${String(b)} is not a constructor or nul
- Object is not iterable.
AI-assisted analysis of microsoft/TypeScript@b465fdbfe1 (2026-08-12).
Data as JSON: /api/errors/f35b1e86c524b83e.
Report an issue: GitHub.