immich-app/immich · error · BadRequestException

Either password or pinCode is required

Error message

Either password or pinCode is required

What it means

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.

Solutions

  1. Include `pinCode` (current PIN) or `password` (account password) in the request body.
  2. Check the DTO field names against the current API version — a renamed field silently becomes 'missing'.
  3. Add client-side validation that refuses to submit the form until one of the credentials is filled.

Example fix

// before
await api.authenticationApi.changePinCode({ newPinCode });
// after
await api.authenticationApi.changePinCode({ pinCode: currentPin, newPinCode });
Defensive patterns

Strategy: validation

Validate before calling

if (!dto.pinCode && !dto.password) {
  throw new Error('Provide either pinCode or password');
}

Type guard

function hasPinCredential(dto: { pinCode?: string; password?: string }): dto is { pinCode: string } | { password: string } {
  return Boolean(dto.pinCode || dto.password);
}

Prevention

When it happens

Trigger: Calling resetPinCode, changePinCode, or unlockSession with an empty body or a body omitting both `password` and `pinCode` fields.

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

Understand the failure class

Background: "missing required argument" and "the following required arguments were not provided": what required-argument errors mean and how to fix them — this error's family across 20 libraries.

Related errors


AI-assisted analysis of immich-app/immich@f48d4b3321 (2026-09-15). Data as JSON: /api/errors/ecac01afb48e1265. Report an issue: GitHub.

Appendix: source

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

  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,
      email: dto.email,
      name: dto.name,
      password: dto.password,
      storageLabel: 'admin',
    });

    return mapUserAdmin(admin);
  }

  async authenticate({ headers, queryParams, metadata }: ValidateRequest): Promise<AuthDto> {
    const authDto = await this.validate({ headers, queryParams });
    const { adminRoute, sharedLinkRoute, uri } = metadata;

View on GitHub (pinned to f48d4b3321)