{"record":{"id":"e610c91d26b1e4a5","repo":"immich-app/immich","slug":"either-query-or-queryassetid-must-be-set","errorCode":null,"errorMessage":"Either `query` or `queryAssetId` must be set","messagePattern":"Either `query` or `queryAssetId` must be set","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"server/src/services/search.service.ts","lineNumber":174,"sourceCode":"      const key = machineLearning.clip.modelName + dto.query + dto.language;\n      embedding = this.embeddingCache.get(key);\n      if (!embedding) {\n        embedding = await this.machineLearningRepository.encodeText(dto.query, {\n          modelName: machineLearning.clip.modelName,\n          language: dto.language,\n        });\n        this.embeddingCache.set(key, embedding);\n      }\n    } else if (dto.queryAssetId) {\n      await this.requireAccess({ auth, permission: Permission.AssetRead, ids: [dto.queryAssetId] });\n      const getEmbeddingResponse = await this.searchRepository.getEmbedding(dto.queryAssetId);\n      const assetEmbedding = getEmbeddingResponse?.embedding;\n      if (!assetEmbedding) {\n        throw new BadRequestException(`Asset ${dto.queryAssetId} has no embedding`);\n      }\n      embedding = assetEmbedding;\n    } else {\n      throw new BadRequestException('Either `query` or `queryAssetId` must be set');\n    }\n    const page = dto.page ?? 1;\n    const size = dto.size || 100;\n    const { hasNextPage, items } = await this.searchRepository.searchSmart(\n      { page, size },\n      {\n        ...dto,\n        userIds: await userIds,\n        embedding,\n        visibility: dto.visibility ?? (auth.session?.hasElevatedPermission ? undefined : 'not-locked'),\n      },\n    );\n\n    return this.mapResponse(items, hasNextPage ? (page + 1).toString() : null, { auth });\n  }\n\n  async getAssetsByCity(auth: AuthDto): Promise<AssetResponseDto[]> {\n    const userIds = await this.getUserIdsToSearch(auth);","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/search.service.ts#L156-L192","documentation":"Thrown by SearchService.searchSmart when neither dto.query nor dto.queryAssetId is provided. The smart-search endpoint needs a source for the embedding (free text or an anchor asset); with neither, no embedding can be produced. BadRequestException -> HTTP 400.","triggerScenarios":"POST /search/smart with an empty body or only filter fields (page, size, visibility) but no `query` and no `queryAssetId`.","commonSituations":"Client submits the search form before the user typed anything and with no asset selected; DTO validation relaxed at the boundary; a 'search' button wired to fire on focus loss.","solutions":["Require at least one of query or queryAssetId in the DTO validation before calling the service.","Disable the submit button client-side until one field is non-empty.","Use class-validator @ValidateIf or a custom DTO decorator to enforce one-of at the boundary."],"exampleFix":"// before\n} else {\n  throw new BadRequestException('Either `query` or `queryAssetId` must be set');\n}\n\n// DTO-level validation (preferred)\n@ValidateIf((o) => !o.queryAssetId)\n@IsNotEmpty()\n@IsString()\nquery?: string;\n\n@ValidateIf((o) => !o.query)\n@IsNotEmpty()\n@IsString()\nqueryAssetId?: string;","handlingStrategy":"validation","validationCode":"// Require one of query / queryAssetId before calling the service.\nif (!dto.query && !dto.queryAssetId) {\n  throw new Error('Smart search requires a query or queryAssetId');\n}\nawait searchService.searchSmart(auth, dto);","typeGuard":"const hasSmartSearchInput = (d: SmartSearchDto): boolean =>\n  (!!d.query && d.query.trim().length > 0) || !!d.queryAssetId;","tryCatchPattern":null,"preventionTips":["Enforce one-of {query, queryAssetId} at the DTO level with class-validator.","Disable the submit button until one field is populated.","Validate before the network call; this is a client-side bug, not a server one."],"tags":["search","smart-search","validation","dto","nestjs"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}