immich-app/immich · error · BadRequestException
${entity} not found
Error message
${entity} not found What it means
Generic helper findOrFail in server/src/utils/misc.ts: it runs an async find() and throws BadRequestException (`${entity} not found`) if the result is null/undefined. Callers pass an entity label for the message. It is the server-side counterpart of a 404-as-400 used to assert a referenced resource exists.
Source
Thrown at server/src/utils/misc.ts:117
isMachineLearningEnabled(machineLearning) && machineLearning.clip.enabled;
export const isOcrEnabled = (machineLearning: SystemConfig['machineLearning']) =>
isMachineLearningEnabled(machineLearning) && machineLearning.ocr.enabled;
export const isFacialRecognitionEnabled = (machineLearning: SystemConfig['machineLearning']) =>
isMachineLearningEnabled(machineLearning) && machineLearning.facialRecognition.enabled;
export const isDuplicateDetectionEnabled = (machineLearning: SystemConfig['machineLearning']) =>
isSmartSearchEnabled(machineLearning) && machineLearning.duplicateDetection.enabled;
export const isFaceImportEnabled = (metadata: SystemConfig['metadata']) => metadata.faces.import;
export const isConnectionAborted = (error: Error | any) => error.code === 'ECONNABORTED';
export const handlePromiseError = <T>(promise: Promise<T>, logger: LoggingRepository): void => {
promise.catch((error: Error | any) => logger.error(`Promise error: ${error}`, error?.stack));
};
export const findOrFail = async <T>(find: () => Promise<T>, entity: string): Promise<NonNullable<T>> => {
const value = await find();
if (!value) {
throw new BadRequestException(`${entity} not found`);
}
return value;
};
export async function* batched<T>(items: AsyncIterable<T>, size = JOBS_ASSET_PAGINATION_SIZE): AsyncGenerator<T[]> {
let batch: T[] = [];
for await (const item of items) {
batch.push(item);
if (batch.length >= size) {
yield batch;
batch = [];
}
}
if (batch.length > 0) {View on GitHub (pinned to 199723261c)
Solutions
- Inspect the error's entity name to know which resource was missing, then verify that id exists for the caller.
- Refresh the client-side list/cache and retry with a valid id.
- If the resource was deleted intentionally, update the UI to remove the stale reference.
Example fix
// the throw is generic; fix the caller, e.g. // before const person = await findOrFail(() => repo.getByPersonId(maybeId), 'Person'); // after const exists = await repo.existsByPersonId(maybeId); if (!exists) return null; const person = await findOrFail(() => repo.getByPersonId(maybeId), 'Person');
Defensive patterns
Strategy: try-catch
Validate before calling
// pre-check existence using the same finder before calling findOrFail
const candidate = await find();
if (!candidate) { /* handle missing resource gracefully */ } Type guard
const isPresent = <T>(v: T | null | undefined): v is T => v != null;
Try / catch
try { return await findOrFail(() => repo.getById(id), 'Entity'); }
catch (e) { if (e instanceof BadRequestException && /not found$/.test(e.message)) return null; throw e; } Prevention
- Validate resource ids at the API boundary before lookups.
- Where possible use a nullable-returning finder and branch, reserving findOrFail for genuinely invariant cases.
When it happens
Trigger: Any code path that calls findOrFail(findFn, 'Entity') where findFn resolves to null/undefined — e.g. looking up a person, partner, album, or shared link by id that does not exist or is not visible to the caller.
Common situations: Client passes an id that was deleted or never existed; permission-filtered query returns nothing because the resource belongs to another user; race condition where the resource is deleted between listing and the operation.
Related errors
- Library ${id} not found
- Notification not found
- Asset not found or asset is not a video
- Both assets must exist
- Metadata with key "${key}" not found for asset with id "${id
AI-assisted analysis of immich-app/immich@199723261c (2026-08-12).
Data as JSON: /api/errors/4368869ec6fd31d7.
Report an issue: GitHub.