immich-app/immich · error · UnauthorizedException

Missing JWT Token

Error message

Missing JWT Token

What it means

The maintenance worker login(jwt?) requires a JWT; if the caller provides none it throws UnauthorizedException('Missing JWT Token'). This is the maintenance-mode auth entry: callers must supply the maintenance JWT (obtained out-of-band or via token login) to perform further maintenance actions.

Source

Thrown at server/src/maintenance/maintenance-worker.service.ts:262

    return this.login(jwtToken);
  }

  async status(potentiallyJwt?: string): Promise<MaintenanceStatusResponseDto> {
    try {
      await this.login(potentiallyJwt);
      return this.getStatus();
    } catch {
      return this.getPublicStatus();
    }
  }

  detectPriorInstall(): Promise<MaintenanceDetectInstallResponseDto> {
    return detectPriorInstall(this.storageRepository);
  }

  async login(jwt?: string): Promise<MaintenanceAuthDto> {
    if (!jwt) {
      throw new UnauthorizedException('Missing JWT Token');
    }

    try {
      const result = await jwtVerify<MaintenanceAuthDto>(jwt, new TextEncoder().encode(this.secret));
      return result.payload;
    } catch {
      throw new UnauthorizedException('Invalid JWT Token');
    }
  }

  async setAction(action: SetMaintenanceModeDto) {
    this.setStatus({
      active: true,
      action: action.action,
    });

    await this.runAction(action);
  }

View on GitHub (pinned to 199723261c)

Solutions

  1. Provide the maintenance JWT in the request (typically Authorization: Bearer <jwt>).
  2. Obtain a fresh maintenance token through the documented bootstrap flow before calling login.
  3. Ensure any proxy/gateway in front forwards the Authorization header unchanged.
Defensive patterns

Strategy: validation

Validate before calling

if (!jwt) {
  throw new Error('Maintenance JWT is required');
}
await fetch(`${baseUrl}/maintenance/login`, {
  method: 'POST',
  headers: { Authorization: `Bearer ${jwt}` },
});

Type guard

const hasJwt = (headers: Record<string,string>): boolean =>
  Boolean(headers['Authorization'] || headers['authorization']);

Try / catch

try {
  await login(jwt);
} catch (e) {
  if ((e as Error).status === 401 && /Missing JWT/.test((e as Error).message)) {
    // prompt operator for the maintenance token
  } else throw e;
}

Prevention

When it happens

Trigger: Calling POST /maintenance/login (or the worker login) with no Authorization header / no JWT argument at all.

Common situations: Operator connects to the maintenance API without copying the token; a client strips the auth header; cookie-based session expired and no JWT fallback supplied.

Related errors


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