immich-app/immich · error · BadRequestException

This endpoint can only be used with a session token

Error message

This endpoint can only be used with a session token

What it means

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.

Source

Thrown at server/src/services/auth.service.ts:590

          });
        }
      }

      return {
        user: session.user,
        session: {
          id: session.id,
          hasElevatedPermission,
        },
      };
    }

    throw new UnauthorizedException('Invalid user token');
  }

  async unlockSession(auth: AuthDto, dto: SessionUnlockDto): Promise<void> {
    if (!auth.session) {
      throw new BadRequestException('This endpoint can only be used with a session token');
    }

    const user = await this.userRepository.getForPinCode(auth.user.id);
    this.validatePinCode(user, { pinCode: dto.pinCode });

    await this.sessionRepository.update(auth.session.id, {
      pinExpiresAt: DateTime.now().plus({ minutes: 15 }).toJSDate(),
    });
  }

  async lockSession(auth: AuthDto): Promise<void> {
    if (!auth.session) {
      throw new BadRequestException('This endpoint can only be used with a session token');
    }

    await this.sessionRepository.update(auth.session.id, { pinExpiresAt: null });
  }

View on GitHub (pinned to 199723261c)

Solutions

  1. Ensure the client uses a session token (from login) for unlock, not an API key or share link.
  2. Guard the UI so the unlock flow is only reachable when a locked session is actually present.
  3. Inspect the request: confirm an Authorization: Bearer <session-token> or session cookie is attached.

Example fix

// before
if (auth.user) {
  await authService.unlockSession(auth, { pinCode });
}

// after
if (!auth.session) {
  throw new BadRequestException('Unlock requires a session token; re-authenticate via login.');
}
await authService.unlockSession(auth, { pinCode });
Defensive patterns

Strategy: validation

Validate before calling

if (!auth.session) {
  throw new BadRequestException('Unlock requires a session token; log in first.');
}

Type guard

function hasSession(auth: AuthDto): auth is AuthDto & { session: { id: string } } {
  return !!auth.session && typeof auth.session.id === 'string';
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


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