{"record":{"id":"b90e219a791061cd","repo":"immich-app/immich","slug":"invalid-jwt-token","errorCode":null,"errorMessage":"Invalid JWT Token","messagePattern":"Invalid JWT Token","errorType":"http","errorClass":"UnauthorizedException","httpStatus":401,"severity":"error","filePath":"server/src/maintenance/maintenance-worker.service.ts","lineNumber":269,"sourceCode":"    } catch {\n      return this.getPublicStatus();\n    }\n  }\n\n  detectPriorInstall(): Promise<MaintenanceDetectInstallResponseDto> {\n    return detectPriorInstall(this.storageRepository);\n  }\n\n  async login(jwt?: string): Promise<MaintenanceAuthDto> {\n    if (!jwt) {\n      throw new UnauthorizedException('Missing JWT Token');\n    }\n\n    try {\n      const result = await jwtVerify<MaintenanceAuthDto>(jwt, new TextEncoder().encode(this.secret));\n      return result.payload;\n    } catch {\n      throw new UnauthorizedException('Invalid JWT Token');\n    }\n  }\n\n  async setAction(action: SetMaintenanceModeDto) {\n    this.setStatus({\n      active: true,\n      action: action.action,\n    });\n\n    await this.runAction(action);\n  }\n\n  async runAction(action: SetMaintenanceModeDto) {\n    switch (action.action) {\n      case MaintenanceAction.Start:\n      case MaintenanceAction.SelectDatabaseRestore: {\n        return;\n      }","sourceCodeStart":251,"sourceCodeEnd":287,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/maintenance/maintenance-worker.service.ts#L251-L287","documentation":"After a JWT is present, login runs jwtVerify against this.secret. Any verification failure (bad signature, expired, malformed, wrong audience) is swallowed and rethrown as UnauthorizedException('Invalid JWT Token'). The original cause is intentionally hidden to avoid leaking token-validation internals.","triggerScenarios":"Submitting a maintenance JWT that is expired, signed with a different secret, truncated, or otherwise malformed to the maintenance login.","commonSituations":"Server secret rotated since the token was issued; clock skew between client and server causing premature expiry; token copy-paste error; token from a different Immich instance.","solutions":["Re-issue the maintenance JWT against the current server secret.","Verify system clock/NTP is correct on both client and server to avoid spurious exp/nbf failures.","Confirm the token was generated by the same Immich instance/secret it is being verified against."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":"import jwtDecode from 'jwt-decode';\nconst decoded = jwtDecode<{ exp?: number }>(jwt);\nif (decoded.exp && decoded.exp * 1000 < Date.now()) {\n  throw new Error('Maintenance token expired; re-issue it.');\n}","typeGuard":"const looksLikeJwt = (s: string): boolean =>\n  typeof s === 'string' && s.split('.').length === 3;","tryCatchPattern":"try {\n  await login(jwt);\n} catch (e) {\n  if (/Invalid JWT/.test((e as Error).message)) {\n    // re-issue token from the current server secret and retry\n  } else throw e;\n}","preventionTips":["Issue tokens against the live server secret and rotate deliberately.","Keep client/server clocks synced via NTP to avoid premature expiry.","Do not reuse a token across instances with different secrets."],"tags":["maintenance","authentication","jwt","authorization"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}