immich-app/immich · warning · BadRequestException
User does not have a PIN code
Error message
User does not have a PIN code
What it means
BadRequestException (HTTP 400) thrown by the private validatePinCode helper when user.pinCode is null. It guards resetPinCode, changePinCode, and unlockSession; none of these operations make sense without an existing PIN. The check runs before any credential verification, so callers cannot use reset/change/unlock to bootstrap a PIN.
Source
Thrown at server/src/services/auth.service.ts:183
await this.userRepository.update(auth.user.id, { pinCode: null });
await this.sessionRepository.lockAll(auth.user.id);
}
async changePinCode(auth: AuthDto, dto: PinCodeChangeDto) {
const user = await this.userRepository.getForPinCode(auth.user.id);
this.validatePinCode(user, dto);
const hashed = await this.cryptoRepository.hashBcrypt(dto.newPinCode, SALT_ROUNDS);
await this.userRepository.update(auth.user.id, { pinCode: hashed });
}
private validatePinCode(
user: { pinCode: string | null; password: string | null },
dto: { pinCode?: string; password?: string },
) {
if (!user.pinCode) {
throw new BadRequestException('User does not have a PIN code');
}
if (dto.password) {
if (!this.validateSecret(dto.password, user.password)) {
throw new BadRequestException('Wrong password');
}
} else if (dto.pinCode) {
if (!this.validateSecret(dto.pinCode, user.pinCode)) {
throw new BadRequestException('Wrong PIN code');
}
} else {
throw new BadRequestException('Either password or pinCode is required');
}
}
async adminSignUp(dto: SignUpDto): Promise<UserAdminResponseDto> {
const admin = await this.createUser({
isAdmin: true,View on GitHub (pinned to 199723261c)
Solutions
- Call GET /auth/status and check `pinCode` before invoking reset/change/unlock; if false, hide those controls.
- If the user wants a PIN, POST /auth/pin-code to set one first.
- If the UI expected a PIN to exist, force a re-fetch of auth status on each app foreground.
- Verify no other admin/tool cleared the pinCode column out of band.
Example fix
// before
await api.authApi.unlockSession({ pinCode: '123456' });
// after
const status = await api.authApi.getAuthStatus();
if (!status.pinCode) {
throw new Error('No PIN configured; call setup first');
}
await api.authApi.unlockSession({ pinCode: '123456' }); Defensive patterns
Strategy: validation
Validate before calling
async function ensurePinExists() {
const { pinCode } = await api.authApi.getAuthStatus();
if (!pinCode) throw new Error('No PIN configured');
} Type guard
function hasPinConfigured(status: AuthStatusResponseDto): status is AuthStatusResponseDto & { pinCode: true } {
return status.pinCode === true;
} Try / catch
try {
await api.authApi.resetPinCode({ password });
} catch (e) {
if (e.response?.data?.message === 'User does not have a PIN code') {
hidePinControls(); // no PIN to reset
} else throw e;
} Prevention
- Hide reset/change/unlock controls unless auth status reports pinCode=true.
- Refetch auth status after every PIN setup/reset.
- For OAuth-only users (no password), expect a PIN-less state by default.
When it happens
Trigger: DELETE /auth/pin-code, PUT /auth/pin-code, or POST /auth/session/unlock against a user whose pinCode column is null. Common after a fresh install, after an admin reset the user's PIN, or when the user authenticates only via OAuth and never set a PIN.
Common situations: User authenticates by OAuth/password only and the client assumes a PIN exists because a 'locked' UI was shown; PIN was reset server-side but the client cached stale auth-status state; wrong endpoint used in client flow.
Related errors
- User already has a PIN code
- Either password or pinCode is required
- Wrong PIN code
- Forbidden
- Missing required permission: ${requestedPermission}
AI-assisted analysis of immich-app/immich@199723261c (2026-08-12).
Data as JSON: /api/errors/5c703bc3221e70f2.
Report an issue: GitHub.