{"record":{"id":"ed382789220ab7c9","repo":"angular/components","slug":"cdk-component-harness-query-must-contain-at-least","errorCode":null,"errorMessage":"CDK Component harness query must contain at least one element.","messagePattern":"CDK Component harness query must contain at least one element\\.","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src/cdk/testing/harness-environment.ts","lineNumber":377,"sourceCode":"  protected abstract createTestElement(element: E): TestElement;\n\n  /** Creates a `HarnessEnvironment` rooted at the given raw element. */\n  protected abstract createEnvironment(element: E): HarnessEnvironment<E>;\n\n  /**\n   * Gets a list of all elements matching the given selector under this environment's root element.\n   */\n  protected abstract getAllRawElements(selector: string): Promise<E[]>;\n\n  /**\n   * Matches the given raw elements with the given list of element and harness queries to produce a\n   * list of matched harnesses and test elements.\n   */\n  private async _getAllHarnessesAndTestElements<T extends (HarnessQuery<any> | string)[]>(\n    queries: T,\n  ): Promise<LocatorFnResult<T>[]> {\n    if (!queries.length) {\n      throw Error('CDK Component harness query must contain at least one element.');\n    }\n\n    const {allQueries, harnessQueries, elementQueries, harnessTypes} = _parseQueries(queries);\n\n    // Combine all of the queries into one large comma-delimited selector and use it to get all raw\n    // elements matching any of the individual queries.\n    const rawElements = await this.getAllRawElements(\n      [...elementQueries, ...harnessQueries.map(predicate => predicate.getSelector())].join(','),\n    );\n\n    // If every query is searching for the same harness subclass, we know every result corresponds\n    // to an instance of that subclass. Likewise, if every query is for a `TestElement`, we know\n    // every result corresponds to a `TestElement`. Otherwise we need to verify which result was\n    // found by which selector so it can be matched to the appropriate instance.\n    const skipSelectorCheck =\n      (elementQueries.length === 0 && harnessTypes.size === 1) || harnessQueries.length === 0;\n\n    const perElementMatches = await parallel(() =>","sourceCodeStart":359,"sourceCodeEnd":395,"githubUrl":"https://github.com/angular/components/blob/0411926e7d8ae06b32236ec1048a888cfad5abf2/src/cdk/testing/harness-environment.ts#L359-L395","documentation":"Thrown by _getAllHarnessesAndTestElements, the shared implementation behind locatorFor, locatorForOptional, and locatorForAll, when the queries array is empty. A locator must be built from at least one harness or element query; an empty query list is always a caller bug.","triggerScenarios":"Calling loader.locatorFor() / locatorForOptional() / locatorForAll() with no arguments, or spreading a conditionally-built array of queries that ended up empty.","commonSituations":"Programmatic query building: const queries = shouldInclude ? [ChildHarness] : []; then locatorFor(...queries) with the flag false; also hardcoded locatorFor() calls after refactoring away the only query.","solutions":["Pass at least one harness or selector query to the locator factory.","Guard programmatic call sites: only create the locator when queries.length > 0.","Provide a fallback query (e.g. a selector string) when the primary list may be empty."],"exampleFix":"// before\nconst q = shouldInclude ? [ChildHarness] : [];\nconst lf = loader.locatorFor(...q);\n// after\nconst q = shouldInclude ? [ChildHarness] : [FallbackHarness];\nconst lf = loader.locatorFor(...q);","handlingStrategy":"validation","validationCode":"if (!queries.length) throw new Error('locator requires at least one query');\nconst lf = loader.locatorFor(...queries);","typeGuard":"const hasQueries = <T>(q: T[]): q is [T, ...T[]] => q.length > 0;","tryCatchPattern":"try { return await loader.locatorFor(...queries)(); } catch (e) { if ((e as Error).message.includes('must contain at least one element')) throw new Error('Empty harness query list', {cause: e}); throw e; }","preventionTips":["Never spread possibly-empty arrays into locatorFor* without a guard","Provide default/fallback queries when building lists conditionally"],"tags":["angular-cdk","testing","component-harness","argument-validation"],"backgroundTag":"empty-query-argument","analyzedSha":"0411926e7d8ae06b32236ec1048a888cfad5abf2","analyzedAt":"2026-08-31T11:58:23.400Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}