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
- List all metadata for the asset (GET /api/assets/:id/metadata) to confirm the exact key name
- Check key spelling and casing — keys are matched exactly
- Handle 400 gracefully and return null/default when the key is optional
- 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
- Confirm exact key names via the asset's metadata list endpoint
- Match keys case-sensitively and store them as constants
- Treat optional metadata keys as nullable in client models
- Run the ML/metadata job before expecting derived keys to exist
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
- Duplicate items are not allowed
- Notification not found
- Asset dimensions are not available for editing
- Asset does not have valid dimensions
- Asset media not found
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)