immich-app/immich · error · BadRequestException

Invalid job name

Error message

Invalid job name

What it means

asJobItem is a switch over ManualJobName values that maps each manual job handle to a concrete JobName plus payload; the default branch throws this error when a caller passes a manual-job identifier that has no case in the switch. It fires whenever a new ManualJobName enum member is added without extending the mapping, or when a stale/unknown string is submitted through the create flow, so it signals a programming/migration gap rather than user input problems.

Solutions

  1. Add a case for the unrecognized ManualJobName in asJobItem mapping it to the correct JobName and data payload
  2. Verify the client/API is sending a ManualJobName value that exists in the current enum version
  3. Check for enum drift between the API version receiving the request and the server code handling it
Defensive patterns

Strategy: validation

When it happens

Trigger: Thrown at server/src/services/job.service.ts:74 when the library encounters an invalid state.

Common situations: See trigger scenarios.


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

Appendix: source

Thrown at server/src/services/job.service.ts:74

    case ManualJobName.IntegrityChecksumFilesRefresh: {
      return { name: JobName.IntegrityChecksumFiles, data: { refreshOnly: true } };
    }

    case ManualJobName.IntegrityMissingFilesDeleteAll: {
      return { name: JobName.IntegrityDeleteReportType, data: { type: IntegrityReport.MissingFile } };
    }

    case ManualJobName.IntegrityUntrackedFilesDeleteAll: {
      return { name: JobName.IntegrityDeleteReportType, data: { type: IntegrityReport.UntrackedFile } };
    }

    case ManualJobName.IntegrityChecksumFilesDeleteAll: {
      return { name: JobName.IntegrityDeleteReportType, data: { type: IntegrityReport.ChecksumFail } };
    }

    default: {
      throw new BadRequestException('Invalid job name');
    }
  }
};

@Injectable()
export class JobService extends BaseService {
  async create(dto: JobCreateDto): Promise<void> {
    await this.jobRepository.queue(asJobItem(dto));
  }

  @OnEvent({ name: 'JobRun' })
  async onJobRun(...[queueName, job]: ArgsOf<'JobRun'>) {
    try {
      await this.eventRepository.emit('JobStart', queueName, job);
      const response = await this.jobRepository.run(job);
      await this.eventRepository.emit('JobSuccess', { job, response });
      if (response && typeof response === 'string' && [JobStatus.Success, JobStatus.Skipped].includes(response)) {
        await this.onDone(job);

View on GitHub (pinned to e55ac299a4)