immich-app/immich · error · UnauthorizedException
Missing JWT Token
Error message
Missing JWT Token
What it means
The maintenance worker's login() method requires a maintenance JWT string; if the argument is absent or empty it throws UnauthorizedException('Missing JWT Token') before any verification is attempted. It is the guard clause distinguishing 'no token supplied' from the separate 'Invalid JWT Token' failure in the jwtVerify catch block.
Solutions
- Perform a maintenance login first and pass the returned token into login()/authenticate.
- Attach the JWT in the request (Authorization: Bearer <token> or the maintenance cookie) before calling protected maintenance endpoints.
- Check proxy/middleware configuration so the Authorization header is forwarded to Immich.
- Confirm the client sends the correct field the controller reads (e.g. token in MaintenanceLoginDto body), not a missing/renamed property.
Example fix
// before
await maintenanceWorker.login(); // UnauthorizedException: Missing JWT Token
// after
if (!jwt) throw new Error('Login first to obtain a maintenance token');
await maintenanceWorker.login(jwt); Defensive patterns
Strategy: validation
Validate before calling
if (typeof jwt !== 'string' || jwt.length === 0) {
throw new Error('A maintenance JWT is required; perform a maintenance login first');
} Type guard
function hasJwt(t: string | undefined | null): t is string {
return typeof t === 'string' && t.length > 0;
} Prevention
- Always complete a maintenance login to obtain a token before calling authenticate/status.
- Send the token via the Authorization header or maintenance cookie and verify proxies forward it.
- Check client code sends the exact field the controller expects.
- Alert users when no maintenance session exists instead of firing blind requests.
When it happens
Trigger: Calling maintenanceWorkerService.login() (directly or via the authenticate/status callers) with jwt === undefined or an empty string; sending a maintenance login request with no Authorization header / no token parameter so no JWT is passed through.
Common situations: Client forgot to attach the maintenance token obtained from a prior login; header stripped by a reverse proxy; calling the status/authenticate path before ever performing a maintenance login; passing the wrong field name so the token never reaches the handler.
Related errors
AI-assisted analysis of immich-app/immich@e55ac299a4 (2026-09-15).
Data as JSON: /api/errors/2bca211e652cc2fe.
Report an issue: GitHub.
Appendix: 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 e55ac299a4)