{"record":{"id":"ecac01afb48e1265","repo":"immich-app/immich","slug":"either-password-or-pincode-is-required","errorCode":null,"errorMessage":"Either password or pinCode is required","messagePattern":"Either password or pinCode is required","errorType":"http","errorClass":"BadRequestException","httpStatus":400,"severity":"warning","filePath":"server/src/services/auth.service.ts","lineNumber":195,"sourceCode":"\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\n  async authenticate({ headers, queryParams, metadata }: ValidateRequest): Promise<AuthDto> {\n    const authDto = await this.validate({ headers, queryParams });\n    const { adminRoute, sharedLinkRoute, uri } = metadata;","sourceCodeStart":177,"sourceCodeEnd":213,"githubUrl":"https://github.com/immich-app/immich/blob/199723261c6ffa897fec8ccdaea6359e39c37cc3/server/src/services/auth.service.ts#L177-L213","documentation":"BadRequestException (HTTP 400) thrown at the end of validatePinCode when neither dto.password nor dto.pinCode is truthy. The DTO (PinCodeResetSchema/PinCodeChangeSchema) marks both optional, so the runtime check is the only line of defense. Reached only when the user has a PIN set.","triggerScenarios":"PUT /auth/pin-code, DELETE /auth/pin-code, or POST /auth/session/unlock with body that omits both password and pinCode, or sends empty strings. Because PinCodeResetSchema has both as optional, the request passes DTO validation and fails here.","commonSituations":"Client bug submitting an empty form; serialization issue that drops falsy fields; client assumes the session is already elevated and sends no credentials; version mismatch where the client predates the password-or-pinCode requirement.","solutions":["Ensure the request body contains exactly one of `password` or `pinCode` (non-empty).","Upgrade the client to send `pinCode` for unlock flows and `password` for reset flows per the API docs.","Add a client-side check that one credential field is non-empty before POSTing.","If using a generated SDK, regenerate it from the current open-api spec to pick up schema changes."],"exampleFix":"// before\nawait api.authApi.resetPinCode({});\n\n// after\nif (!body.password && !body.pinCode) {\n  throw new Error('password or pinCode required');\n}\nawait api.authApi.resetPinCode(body);","handlingStrategy":"validation","validationCode":"function validatePinResetBody(body: { password?: string; pinCode?: string }) {\n  if (!body.password && !body.pinCode) {\n    throw new Error('password or pinCode required');\n  }\n}","typeGuard":"function hasPinCredential(body: { password?: string; pinCode?: string }): body is { password: string } | { pinCode: string } {\n  return Boolean(body.password) || Boolean(body.pinCode);\n}","tryCatchPattern":"try {\n  await api.authApi.resetPinCode(body);\n} catch (e) {\n  if (e.response?.data?.message === 'Either password or pinCode is required') {\n    showCredentialRequiredError();\n  } else throw e;\n}","preventionTips":["Enforce non-empty password XOR pinCode before submitting.","Disable the submit button until a credential is entered.","Regenerate the SDK after schema changes so optional vs. required fields are clear."],"tags":["auth","pin-code","nestjs","immich","validation"],"backgroundTag":null,"analyzedSha":"199723261c6ffa897fec8ccdaea6359e39c37cc3","analyzedAt":"2026-08-12T04:54:27.085Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}