{"record":{"id":"497e40c957b2aeb3","repo":"angular/components","slug":"listkeymanager-constructed-with-a-signal-must-rece","errorCode":null,"errorMessage":"ListKeyManager constructed with a signal must receive an injector","messagePattern":"ListKeyManager constructed with a signal must receive an injector","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"src/cdk/a11y/key-manager/list-key-manager.ts","lineNumber":76,"sourceCode":"   */\n  private _skipPredicateFn = (item: T) => item.disabled;\n\n  constructor(items: QueryList<T> | T[] | readonly T[]);\n  constructor(items: Signal<T[]> | Signal<readonly T[]>, injector: Injector);\n  constructor(\n    private _items: QueryList<T> | T[] | readonly T[] | Signal<T[]> | Signal<readonly T[]>,\n    injector?: Injector,\n  ) {\n    // We allow for the items to be an array because, in some cases, the consumer may\n    // not have access to a QueryList of the items they want to manage (e.g. when the\n    // items aren't being collected via `ViewChildren` or `ContentChildren`).\n    if (_items instanceof QueryList) {\n      this._itemChangesSubscription = _items.changes.subscribe((newItems: QueryList<T>) =>\n        this._itemsChanged(newItems.toArray()),\n      );\n    } else if (isSignal(_items)) {\n      if (!injector && (typeof ngDevMode === 'undefined' || ngDevMode)) {\n        throw new Error('ListKeyManager constructed with a signal must receive an injector');\n      }\n\n      this._effectRef = effect(() => this._itemsChanged(_items()), {injector});\n    }\n  }\n\n  /**\n   * Stream that emits any time the TAB key is pressed, so components can react\n   * when focus is shifted off of the list.\n   */\n  readonly tabOut = new Subject<void>();\n\n  /** Stream that emits whenever the active item of the list manager changes. */\n  readonly change = new Subject<number>();\n\n  /**\n   * Sets the predicate function that determines which items should be skipped by the\n   * list key manager.","sourceCodeStart":58,"sourceCodeEnd":94,"githubUrl":"https://github.com/angular/components/blob/0411926e7d8ae06b32236ec1048a888cfad5abf2/src/cdk/a11y/key-manager/list-key-manager.ts#L58-L94","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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."],"exampleFix":"// before\nthis.keyManager = new ListKeyManager(this.itemsSignal);\n// after\nprivate injector = inject(Injector);\nthis.keyManager = new ListKeyManager(this.itemsSignal, this.injector);","handlingStrategy":"validation","validationCode":"if (isSignal(items) && !injector) {\n  throw new Error('Provide injector when constructing ListKeyManager with a signal');\n}","typeGuard":"function isSignalLike<T>(v: unknown): v is Signal<T> {\n  return typeof v === 'function' && (v as Signal<T>)[SIGNAL] !== undefined;\n}","tryCatchPattern":"try {\n  this.keyManager = new ListKeyManager(this.itemsSignal, this.injector);\n} catch (e) {\n  if ((e as Error).message.includes('must receive an injector')) {\n    this.keyManager = new ListKeyManager(this.itemsSignal, inject(Injector));\n  } else throw e;\n}","preventionTips":["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."],"tags":["angular","cdk","a11y","list-key-manager","injector","signals"],"backgroundTag":"missing-injection-context","analyzedSha":"0411926e7d8ae06b32236ec1048a888cfad5abf2","analyzedAt":"2026-08-31T11:58:23.400Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}