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

  1. Inspect the error's entity name to know which resource was missing, then verify that id exists for the caller.
  2. Refresh the client-side list/cache and retry with a valid id.
  3. 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

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


AI-assisted analysis of immich-app/immich@199723261c (2026-08-12). Data as JSON: /api/errors/4368869ec6fd31d7. Report an issue: GitHub.