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
- Send a non-empty `query` string for text semantic search
- Send `queryAssetId` for similarity-by-asset search
- Add client-side validation requiring exactly one of the two before calling the API
- 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
- Validate smart-search DTOs client-side before sending
- Never submit an empty search box to smart search
- Route filter-only requests to metadata search
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
- Asset dimensions are not available for editing
- Asset has no embedding
- assetIds, albumId, or userId is required
- At least two people are required for merging
- Cannot add another owner
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)