immich-app/immich · error · BadRequestException

Metadata with key " " not found for asset with id

Error message

Metadata with key "${key}" not found for asset with id "${id}"

What it means

getMetadataByKey looked up a single metadata item by asset id and key, and the repository returned nothing. The service throws BadRequestException naming both the missing key and asset id. The key simply does not exist on that asset.

Solutions

  1. List all metadata for the asset (GET /api/assets/:id/metadata) to confirm the exact key name
  2. Check key spelling and casing — keys are matched exactly
  3. Handle 400 gracefully and return null/default when the key is optional
  4. If the key should exist, run the relevant job (e.g. facial recognition, metadata extraction) for the asset first

Example fix

// before
const item = await api.getMetadataByKey(id, 'Face-1');
// after
const all = await api.getAssetMetadata(id);
const item = all.find((m) => m.key === 'face-1') ?? null;
Defensive patterns

Strategy: fallback

Validate before calling

const all = await api.getAssetMetadata(id);
if (!all.some((m) => m.key === key)) return null; // key absent, skip lookup

Try / catch

try { return await api.getMetadataByKey(id, key); } catch (e) { if (String(e?.response?.data?.message).includes('not found')) return null; throw e; }

Prevention

When it happens

Trigger: GET /api/assets/:id/metadata/key/:key where no row in asset_metadata matches (assetId, key) — the key was never set, was deleted, or is misspelled.

Common situations: Requesting keys like 'face-1' that only exist when ML face detection has run; typos or casing differences in the key; assets processed before a metadata feature was enabled; key removed by a later edit.

Understand the failure class

Background: Record Not Found Errors: "not found", RecordNotFound, and "was not found" — what they mean and how to fix them — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at server/src/services/asset.service.ts:438

    const uniqueKeys = new Set<string>();
    for (const { key } of dto.items) {
      if (uniqueKeys.has(key)) {
        throw new BadRequestException(`Duplicate items are not allowed: "${key}"`);
      }

      uniqueKeys.add(key);
    }

    return this.assetRepository.upsertMetadata(id, dto.items);
  }

  async getMetadataByKey(auth: AuthDto, id: string, key: string): Promise<AssetMetadataResponseDto> {
    await this.requireAccess({ auth, permission: Permission.AssetRead, ids: [id] });

    const item = await this.assetRepository.getMetadataByKey(id, key);
    if (!item) {
      throw new BadRequestException(`Metadata with key "${key}" not found for asset with id "${id}"`);
    }
    return item;
  }

  async deleteMetadataByKey(auth: AuthDto, id: string, key: string): Promise<void> {
    await this.requireAccess({ auth, permission: Permission.AssetUpdate, ids: [id] });
    return this.assetRepository.deleteMetadataByKey(id, key);
  }

  async deleteBulkMetadata(auth: AuthDto, dto: AssetMetadataBulkDeleteDto) {
    await this.requireAccess({ auth, permission: Permission.AssetUpdate, ids: dto.items.map((item) => item.assetId) });
    await this.assetRepository.deleteBulkMetadata(dto.items);
  }

  async run(auth: AuthDto, dto: AssetJobsDto) {
    await this.requireAccess({ auth, permission: Permission.AssetUpdate, ids: dto.assetIds });

    const jobs: JobItem[] = [];

View on GitHub (pinned to e55ac299a4)