angular/components · error · Error
ListKeyManager constructed with a signal must receive an inj
Error message
ListKeyManager constructed with a signal must receive an injector
What it means
ListKeyManager can take a Signal as its items source, but signal-based item changes are tracked with an effect() that requires an injection context. When constructed with a signal outside an injection context, an Injector must be passed explicitly; otherwise dev-mode throws this error.
Source
Thrown at src/cdk/a11y/key-manager/list-key-manager.ts:76
*/
private _skipPredicateFn = (item: T) => item.disabled;
constructor(items: QueryList<T> | T[] | readonly T[]);
constructor(items: Signal<T[]> | Signal<readonly T[]>, injector: Injector);
constructor(
private _items: QueryList<T> | T[] | readonly T[] | Signal<T[]> | Signal<readonly T[]>,
injector?: Injector,
) {
// We allow for the items to be an array because, in some cases, the consumer may
// not have access to a QueryList of the items they want to manage (e.g. when the
// items aren't being collected via `ViewChildren` or `ContentChildren`).
if (_items instanceof QueryList) {
this._itemChangesSubscription = _items.changes.subscribe((newItems: QueryList<T>) =>
this._itemsChanged(newItems.toArray()),
);
} else if (isSignal(_items)) {
if (!injector && (typeof ngDevMode === 'undefined' || ngDevMode)) {
throw new Error('ListKeyManager constructed with a signal must receive an injector');
}
this._effectRef = effect(() => this._itemsChanged(_items()), {injector});
}
}
/**
* Stream that emits any time the TAB key is pressed, so components can react
* when focus is shifted off of the list.
*/
readonly tabOut = new Subject<void>();
/** Stream that emits whenever the active item of the list manager changes. */
readonly change = new Subject<number>();
/**
* Sets the predicate function that determines which items should be skipped by the
* list key manager.View on GitHub (pinned to 0411926e7d)
Solutions
- Pass an injector: new ListKeyManager(itemsSignal, injector).
- Create the manager inside an injection context, e.g. in a field initializer of an Injectable/component or within runInInjectionContext.
- Use inject(Injector) in the class and forward it to the constructor.
- Only construct in dev-bypassing prod builds to hide it is not a fix — always supply the injector for signals.
Example fix
// before this.keyManager = new ListKeyManager(this.itemsSignal); // after private injector = inject(Injector); this.keyManager = new ListKeyManager(this.itemsSignal, this.injector);
Defensive patterns
Strategy: validation
Validate before calling
if (isSignal(items) && !injector) {
throw new Error('Provide injector when constructing ListKeyManager with a signal');
} Type guard
function isSignalLike<T>(v: unknown): v is Signal<T> {
return typeof v === 'function' && (v as Signal<T>)[SIGNAL] !== undefined;
} Try / catch
try {
this.keyManager = new ListKeyManager(this.itemsSignal, this.injector);
} catch (e) {
if ((e as Error).message.includes('must receive an injector')) {
this.keyManager = new ListKeyManager(this.itemsSignal, inject(Injector));
} else throw e;
} Prevention
- Always construct ListKeyManager inside an injection context (field initializer or constructor with inject()).
- Pass the injector explicitly whenever items are a signal.
- Add a code-review rule/comment for signal-based key managers.
When it happens
Trigger: new ListKeyManager(mySignal) called outside an injection context (e.g. in a service constructed manually, a plain function, or a test) without passing {injector} or constructing within inject()/field initializer.
Common situations: Instantiating the key manager in a non-DI service helper; upgrading from QueryList/array items to a signal and forgetting the new injector parameter; running in tests with runInInjectionContext omitted.
Related errors
- KeyManager items in typeahead mode must implement the `getLa
- ListKeyManager items in typeahead mode must implement the `g
- CdkDropList could not find an element container matching the
- Invalid DOM structure for drop list. Alternate container ele
- Invalid DOM structure for drop list. All items must be place
AI-assisted analysis of angular/components@0411926e7d (2026-08-31).
Data as JSON: /api/errors/497e40c957b2aeb3.
Report an issue: GitHub.