{"record":{"id":"2366b3c4ee4e77b9","repo":"immich-app/immich","slug":"this-endpoint-can-only-be-used-with-a-session-toke","errorCode":null,"errorMessage":"This endpoint can only be used with a session token","messagePattern":"This endpoint can only be used with a session token","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/auth.service.ts","lineNumber":590,"sourceCode":"          });\n        }\n      }\n\n      return {\n        user: session.user,\n        session: {\n          id: session.id,\n          hasElevatedPermission,\n        },\n      };\n    }\n\n    throw new UnauthorizedException('Invalid user token');\n  }\n\n  async unlockSession(auth: AuthDto, dto: SessionUnlockDto): Promise<void> {\n    if (!auth.session) {\n      throw new BadRequestException('This endpoint can only be used with a session token');\n    }\n\n    const user = await this.userRepository.getForPinCode(auth.user.id);\n    this.validatePinCode(user, { pinCode: dto.pinCode });\n\n    await this.sessionRepository.update(auth.session.id, {\n      pinExpiresAt: DateTime.now().plus({ minutes: 15 }).toJSDate(),\n    });\n  }\n\n  async lockSession(auth: AuthDto): Promise<void> {\n    if (!auth.session) {\n      throw new BadRequestException('This endpoint can only be used with a session token');\n    }\n\n    await this.sessionRepository.update(auth.session.id, { pinExpiresAt: null });\n  }\n","sourceCodeStart":572,"sourceCodeEnd":608,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/auth.service.ts#L572-L608","documentation":"Thrown by AuthService.unlockSession when the incoming AuthDto has no session object. unlockSession needs a real session to update pinExpiresAt, so authenticating with anything other than a session token (an API key or a share link) is a client-side usage error, not an auth failure.","triggerScenarios":"Calling the session-unlock endpoint while authenticated via an API key or a shared link (auth.session is undefined), or calling it with no authentication at all.","commonSituations":"A mobile/desktop client persisted the wrong token type and calls /auth/unlock with an API key; an automation script reuses an API key against an endpoint that only accepts session tokens; a deep link opened the unlock screen with no active session.","solutions":["Ensure the client uses a session token (from login) for unlock, not an API key or share link.","Guard the UI so the unlock flow is only reachable when a locked session is actually present.","Inspect the request: confirm an Authorization: Bearer <session-token> or session cookie is attached."],"exampleFix":"// before\nif (auth.user) {\n  await authService.unlockSession(auth, { pinCode });\n}\n\n// after\nif (!auth.session) {\n  throw new BadRequestException('Unlock requires a session token; re-authenticate via login.');\n}\nawait authService.unlockSession(auth, { pinCode });","handlingStrategy":"validation","validationCode":"if (!auth.session) {\n  throw new BadRequestException('Unlock requires a session token; log in first.');\n}","typeGuard":"function hasSession(auth: AuthDto): auth is AuthDto & { session: { id: string } } {\n  return !!auth.session && typeof auth.session.id === 'string';\n}","tryCatchPattern":null,"preventionTips":["Only show the PIN-unlock UI when a session token (not an API key) is active.","Authenticate unlock requests with a session cookie/bearer, never an x-api-key.","Track auth kind client-side so endpoint compatibility is known before the call."],"tags":["auth","session","pin-code","bad-request"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}