immich-app/immich · warning · BadRequestException

Asset has no embedding

Error message

Asset ${dto.queryAssetId} has no embedding

What it means

resolveEmbedding supports searching by an existing asset: it fetches the asset's stored smart-search embedding and throws when the asset exists but has no embedding. This happens for assets whose smart-search embedding was never generated or was wiped (e.g. ML disabled previously).

Solutions

  1. Wait for/backfill ML jobs so the asset gets an embedding (re-run machine learning job for the asset)
  2. Pick a different queryAssetId known to have embeddings, or send a text `query` instead
  3. Enable smart search/ML service so new assets get embedded
  4. Check asset status before using it as a similarity seed

Example fix

// before
await api.searchSmart({ queryAssetId: freshUploadId }); // may throw
// after
if (asset.smartSearchStatus === 'ok') {
  await api.searchSmart({ queryAssetId: asset.id });
} else {
  await api.searchSmart({ query: asset.originalFileName });
Defensive patterns

Strategy: fallback

Validate before calling

if (asset.smartSearch?.status !== 'ok') {
  // embedding not ready; use text query instead
  return await api.searchSmart({ query: asset.originalFileName });
}

Type guard

function hasEmbedding(a: { smartSearch?: { status?: string } }): boolean {
  return a.smartSearch?.status === 'ok';
}

Try / catch

try {
  return await searchService.searchSmart(auth, { queryAssetId });
} catch (e) {
  if (e instanceof BadRequestException && /has no embedding/.test(e.message)) {
    return await searchService.searchSmart(auth, { query: fallbackText });
  }
  throw e;
}

Prevention

When it happens

Trigger: Smart search with `queryAssetId` set to an asset that (a) has no embedding row yet because ML hasn't processed it, (b) had embeddings cleared, or (c) was uploaded while smart search was disabled.

Common situations: Building 'find similar' UIs on freshly uploaded assets before ML jobs run; installations that recently enabled smart search so old assets lack embeddings; assets queued but ML service down.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


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

Appendix: source

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

    if (dto.query) {
      const key = machineLearning.clip.modelName + dto.query + dto.language;
      let embedding = this.embeddingCache.get(key);
      if (!embedding) {
        embedding = await this.machineLearningRepository.encodeText(dto.query, {
          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];

View on GitHub (pinned to e55ac299a4)