{"record":{"id":"f29dd38f2e6e5855","repo":"immich-app/immich","slug":"wrong-pin-code","errorCode":null,"errorMessage":"Wrong PIN code","messagePattern":"Wrong PIN code","errorType":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/auth.service.ts","lineNumber":196,"sourceCode":"    const hashed = await this.cryptoRepository.hashBcrypt(dto.newPinCode, SALT_ROUNDS);\n    await this.userRepository.update(auth.user.id, { pinCode: hashed });\n  }\n\n  private validatePinCode(\n    user: { pinCode: string | null; password: string | null },\n    dto: { pinCode?: string; password?: string },\n  ) {\n    if (!user.pinCode) {\n      throw new BadRequestException('User does not have a PIN code');\n    }\n\n    if (dto.password) {\n      if (!this.validateSecret(dto.password, user.password)) {\n        throw new BadRequestException('Wrong password');\n      }\n    } else if (dto.pinCode) {\n      if (!this.validateSecret(dto.pinCode, user.pinCode)) {\n        throw new BadRequestException('Wrong PIN code');\n      }\n    } else {\n      throw new BadRequestException('Either password or pinCode is required');\n    }\n  }\n\n  async adminSignUp(dto: SignUpDto): Promise<UserAdminResponseDto> {\n    const admin = await this.createUser({\n      isAdmin: true,\n      email: dto.email,\n      name: dto.name,\n      password: dto.password,\n      storageLabel: 'admin',\n    });\n\n    return mapUserAdmin(admin);\n  }\n","sourceCodeStart":178,"sourceCodeEnd":214,"githubUrl":"https://github.com/immich-app/immich/blob/f48d4b332127ad365ba256108799ca8f571d2dd5/server/src/services/auth.service.ts#L178-L214","documentation":"When validatePinCode receives a `pinCode` in the dto, it compares it against the stored hashed PIN via validateSecret. A mismatch throws 400 'Wrong PIN code', meaning the supplied plaintext PIN does not match the user's configured PIN.","triggerScenarios":"Calling changePinCode, resetPinCode, or unlockSession with dto.pinCode that fails bcrypt comparison against the stored pinCode hash.","commonSituations":"Typo in PIN entry; PIN was changed on another device so the client's remembered PIN is stale; PIN was reset by an admin; autocorrect/keyboard injecting characters into the PIN field.","solutions":["Re-enter the current PIN carefully and retry; note the PIN is a fixed-digit code, not the account password.","If the PIN is forgotten, use resetPinCode with the account password instead.","Verify the PIN still matches by checking status or unlocking from the device where it was last set."],"exampleFix":"// before\nawait api.authenticationApi.unlockSession({ pinCode: oldPin }); // PIN was rotated\n// after\nconst { hasPin } = await api.authenticationApi.getPinCodeStatus();\nif (hasPin) await api.authenticationApi.resetPinCode({ password }); // PIN forgotten\nelse await api.authenticationApi.unlockSession({ pinCode: currentPin });","handlingStrategy":"validation","validationCode":"if (mode === 'pin' && !/^[0-9]+$/.test(pinCode ?? '')) {\n  throw new Error('Enter the current numeric PIN');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await api.authenticationApi.unlockSession({ pinCode });\n} catch (e) {\n  if (e.status === 400 && e.message === 'Wrong PIN code') {\n    // clear input, re-prompt; offer password-based reset after failures\n  }\n  throw e;\n}","preventionTips":["Clear the PIN field and re-prompt on failure instead of retrying with a stale value.","Sync PIN changes across devices immediately.","Offer resetPinCode with password after repeated PIN failures."],"tags":["pin-code","authentication","credentials"],"backgroundTag":"authentication-required","analyzedSha":"f48d4b332127ad365ba256108799ca8f571d2dd5","analyzedAt":"2026-09-15T07:20:19.675Z","contentChangedAt":"2026-09-15T07:20:19.675Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}