angular/components · error

CDK Component harness query must contain at least one elemen

Error message

CDK Component harness query must contain at least one element.

What it means

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.

Source

Thrown at src/cdk/testing/harness-environment.ts:377

  protected abstract createTestElement(element: E): TestElement;

  /** Creates a `HarnessEnvironment` rooted at the given raw element. */
  protected abstract createEnvironment(element: E): HarnessEnvironment<E>;

  /**
   * Gets a list of all elements matching the given selector under this environment's root element.
   */
  protected abstract getAllRawElements(selector: string): Promise<E[]>;

  /**
   * Matches the given raw elements with the given list of element and harness queries to produce a
   * list of matched harnesses and test elements.
   */
  private async _getAllHarnessesAndTestElements<T extends (HarnessQuery<any> | string)[]>(
    queries: T,
  ): Promise<LocatorFnResult<T>[]> {
    if (!queries.length) {
      throw Error('CDK Component harness query must contain at least one element.');
    }

    const {allQueries, harnessQueries, elementQueries, harnessTypes} = _parseQueries(queries);

    // Combine all of the queries into one large comma-delimited selector and use it to get all raw
    // elements matching any of the individual queries.
    const rawElements = await this.getAllRawElements(
      [...elementQueries, ...harnessQueries.map(predicate => predicate.getSelector())].join(','),
    );

    // If every query is searching for the same harness subclass, we know every result corresponds
    // to an instance of that subclass. Likewise, if every query is for a `TestElement`, we know
    // every result corresponds to a `TestElement`. Otherwise we need to verify which result was
    // found by which selector so it can be matched to the appropriate instance.
    const skipSelectorCheck =
      (elementQueries.length === 0 && harnessTypes.size === 1) || harnessQueries.length === 0;

    const perElementMatches = await parallel(() =>

View on GitHub (pinned to 0411926e7d)

Solutions

  1. Pass at least one harness or selector query to the locator factory.
  2. Guard programmatic call sites: only create the locator when queries.length > 0.
  3. Provide a fallback query (e.g. a selector string) when the primary list may be empty.

Example fix

// before
const q = shouldInclude ? [ChildHarness] : [];
const lf = loader.locatorFor(...q);
// after
const q = shouldInclude ? [ChildHarness] : [FallbackHarness];
const lf = loader.locatorFor(...q);
Defensive patterns

Strategy: validation

Validate before calling

if (!queries.length) throw new Error('locator requires at least one query');
const lf = loader.locatorFor(...queries);

Type guard

const hasQueries = <T>(q: T[]): q is [T, ...T[]] => q.length > 0;

Try / catch

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; }

Prevention

When it happens

Trigger: Calling loader.locatorFor() / locatorForOptional() / locatorForAll() with no arguments, or spreading a conditionally-built array of queries that ended up empty.

Common situations: Programmatic query building: const queries = shouldInclude ? [ChildHarness] : []; then locatorFor(...queries) with the flag false; also hardcoded locatorFor() calls after refactoring away the only query.

Related errors


AI-assisted analysis of angular/components@0411926e7d (2026-08-31). Data as JSON: /api/errors/ed382789220ab7c9. Report an issue: GitHub.