immich-app/immich · warning · BadRequestException

Either `query` or `queryAssetId` must be set

Error message

Either `query` or `queryAssetId` must be set

What it means

resolveEmbedding requires exactly one embedding source: either a text `query` or a `queryAssetId`. When neither is provided it throws this BadRequest, since there is nothing to embed for a smart search.

Solutions

  1. Send a non-empty `query` string for text semantic search
  2. Send `queryAssetId` for similarity-by-asset search
  3. Add client-side validation requiring exactly one of the two before calling the API
  4. Route filter-only requests to metadata search instead of smart search

Example fix

// before
await api.searchSmart({ page: 1 }); // throws
// after
const dto = term ? { query: term } : { queryAssetId: assetId };
await api.searchSmart(dto);
Defensive patterns

Strategy: validation

Validate before calling

if (!dto.query && !dto.queryAssetId) {
  throw new MissingSearchQuery(); // don't call the API
}

Type guard

function hasSearchSource(dto: { query?: string; queryAssetId?: string }): boolean {
  return (typeof dto.query === 'string' && dto.query.length > 0) || typeof dto.queryAssetId === 'string';
}

Try / catch

try {
  return await searchService.searchSmart(auth, dto);
} catch (e) {
  if (e instanceof BadRequestException && /must be set/.test(e.message)) {
    return emptyResult();
  }
  throw e;
}

Prevention

When it happens

Trigger: POST /search/smart (or searchSmart) with a body lacking both `query` and `queryAssetId` — e.g. empty body, or only filter/page/size fields set.

Common situations: Clients clearing the search box and submitting anyway; UI code building the DTO conditionally and dropping the query; API consumers confusing smart search with metadata search parameter names.

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15). Data as JSON: /api/errors/e610c91d26b1e4a5. Report an issue: GitHub.

Appendix: source

Thrown at server/src/services/search.service.ts:353

          modelName: machineLearning.clip.modelName,
          language: dto.language,
        });
        this.embeddingCache.set(key, embedding);
      }
      return embedding;
    }

    if (dto.queryAssetId) {
      await this.requireAccess({ auth, permission: Permission.AssetRead, ids: [dto.queryAssetId] });
      const getEmbeddingResponse = await this.searchRepository.getEmbedding(dto.queryAssetId);
      const assetEmbedding = getEmbeddingResponse?.embedding;
      if (!assetEmbedding) {
        throw new BadRequestException(`Asset ${dto.queryAssetId} has no embedding`);
      }
      return assetEmbedding;
    }

    throw new BadRequestException('Either `query` or `queryAssetId` must be set');
  }

  private async getUserIdsToSearch(auth: AuthDto, visibility?: AssetVisibility): Promise<string[]> {
    // Locked assets are personal. Never include partner IDs, regardless of A's elevated session.
    if (visibility === AssetVisibility.Locked) {
      return [auth.user.id];
    }
    const partnerIds = await getMyPartnerIds({
      userId: auth.user.id,
      repository: this.partnerRepository,
      timelineEnabled: true,
    });
    return [auth.user.id, ...partnerIds];
  }

  private mapResponse(
    assets: MapAsset[],
    options: AssetMapOptions,

View on GitHub (pinned to e55ac299a4)