{"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":"exception","errorClass":"BadRequestException","httpStatus":400,"severity":"error","filePath":"server/src/services/auth.service.ts","lineNumber":199,"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":181,"sourceCodeEnd":217,"githubUrl":"https://github.com/immich-app/immich/blob/f48d4b332127ad365ba256108799ca8f571d2dd5/server/src/services/auth.service.ts#L181-L217","documentation":"validatePinCode requires at least one credential: if the dto contains neither `password` nor `pinCode`, there is nothing to authenticate the PIN operation with, so it throws 400 'Either password or pinCode is required'. This is a request-shape guard, not a credential check.","triggerScenarios":"Calling resetPinCode, changePinCode, or unlockSession with an empty body or a body omitting both `password` and `pinCode` fields.","commonSituations":"Client sending only `newPinCode` and forgetting the confirming credential; DTO field renames after an API version change leaving the old key being dropped by the serializer; partially built request objects in scripts/tests.","solutions":["Include `pinCode` (current PIN) or `password` (account password) in the request body.","Check the DTO field names against the current API version — a renamed field silently becomes 'missing'.","Add client-side validation that refuses to submit the form until one of the credentials is filled."],"exampleFix":"// before\nawait api.authenticationApi.changePinCode({ newPinCode });\n// after\nawait api.authenticationApi.changePinCode({ pinCode: currentPin, newPinCode });","handlingStrategy":"validation","validationCode":"if (!dto.pinCode && !dto.password) {\n  throw new Error('Provide either pinCode or password');\n}","typeGuard":"function hasPinCredential(dto: { pinCode?: string; password?: string }): dto is { pinCode: string } | { password: string } {\n  return Boolean(dto.pinCode || dto.password);\n}","tryCatchPattern":null,"preventionTips":["Require the confirming credential in the form before submit.","Validate request bodies against the current API DTO after version upgrades.","Write tests asserting each PIN endpoint is called with a credential present."],"tags":["validation","missing-field","pin-code"],"backgroundTag":"missing-required-argument","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"}