{"record":{"id":"37136fc53397e311","repo":"passbolt/passbolt_api","slug":"invalid-request-passwords-are-required-for-this-change","errorCode":null,"errorMessage":"Invalid request. Passwords are required for this change.","messagePattern":"Invalid request\\. Passwords are required for this change\\.","errorType":"http","errorClass":"Cake\\Http\\Exception\\BadRequestException","httpStatus":400,"severity":"error","filePath":"plugins/PassboltEe/AccountRecovery/src/Service/AccountRecoveryOrganizationPolicies/AccountRecoveryOrganizationPolicySetService.php","lineNumber":109,"sourceCode":"        if (($isNewKeyProvided && !$isRevokedKeyProvided) || (!$isNewKeyProvided && $isRevokedKeyProvided)) {\n            throw new BadRequestException(__('Invalid request. Keys are required for this change.'));\n        }\n\n        // if key provided or revocation provided\n        $newKey = null;\n        $oldKey = null;\n        $passwords = null;\n        /** @psalm-suppress RedundantCondition */\n        if ($isNewKeyProvided && $isRevokedKeyProvided) {\n            // assert old and new key$newKey\n            $newKey = $this->buildPublicKeyEntityFromDataOrFail($uac);\n            $oldKey = $this->buildRevokedKeyEntityFromDataOrFail($uac);\n\n            // If some existing backups are present\n            // assert new backups are provided\n            if ($this->backupsExists()) {\n                if (!$isPrivateKeyPasswordsProvided) {\n                    throw new BadRequestException(__('Invalid request. Passwords are required for this change.'));\n                }\n                // assert passwords backups format and numbers\n                $passwords = $this->buildPasswordEntitiesFromDataOrFail($uac, $newKey);\n            }\n            $newPolicy->account_recovery_organization_public_key = $newKey;\n        } else {\n            // If key is not changing reuse the old one\n            if (!isset($newPolicy->public_key_id)) {\n                throw new CustomValidationException(__('Could not validate public key data.'), [\n                    'public_key_id' => [\n                        '_required' => __('An organization public key is required.'),\n                    ],\n                ]);\n            } else {\n                if ($newPolicy->public_key_id !== $this->getCurrentPolicyEntity()->public_key_id) {\n                    throw new CustomValidationException(__('Could not validate public key data.'), [\n                        'public_key_id' => [\n                            'notCurrentPublicKeyId' => __('The public_key_id must match current policy public_key_id.'),","sourceCodeStart":91,"sourceCodeEnd":127,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltEe/AccountRecovery/src/Service/AccountRecoveryOrganizationPolicies/AccountRecoveryOrganizationPolicySetService.php#L91-L127","documentation":"This BadRequestException is thrown during an organization key rotation when existing user backups (account_recovery_private_key_passwords rows) are present in the database, but the request does not include the re-encrypted private key passwords. Because the organization private key changes, all stored backups must be re-encrypted against the new key and re-submitted, otherwise existing users would lose recovery capability.","triggerScenarios":"Key rotation request (new public key + revoked key provided) where backupsExists() finds stored private key password backups, but 'account_recovery_private_key_passwords' is absent from the payload. Raised in AccountRecoveryOrganizationPolicySetService::set() at line 109.","commonSituations":"An admin rotates the organization recovery key without first collecting and re-encrypting all users' backup passwords; a script automates rotation assuming an empty backups table; a client version predating the backups-required rule; testing on a instance where backups were created after the payload was built.","solutions":["Before rotating, ensure every user with a backup re-encrypts their private key password data against the new organization public key and include it in 'account_recovery_private_key_passwords'.","If no backups should exist, verify and clear stale rows first — the check triggers only when backupsExists() is true.","Include the full set of passwords (count must match stored backups; see assertPasswordsCount) encrypted with the new key.","Use the official passbolt key-rotation workflow/UI which collects re-encrypted passwords from users before applying rotation."],"exampleFix":"// before (rotation without re-encrypted backups)\nawait passbolt.rotateAccountRecoveryKey({\n  account_recovery_organization_public_key: newArmoredKey,\n  account_recovery_organization_revoked_key: oldArmoredKey\n});\n\n// after (rotation including re-encrypted passwords)\nawait passbolt.rotateAccountRecoveryKey({\n  account_recovery_organization_public_key: newArmoredKey,\n  account_recovery_organization_revoked_key: oldArmoredKey,\n  account_recovery_private_key_passwords: reencryptedPasswords\n});","handlingStrategy":"validation","validationCode":"const current = await passbolt.getAccountRecoveryOrganizationPolicy();\nconst hasBackups = current.backupsCount > 0; // or check via API\nif (hasBackups && !payload.account_recovery_private_key_passwords?.length) {\n  throw new Error('Existing backups detected: include re-encrypted account_recovery_private_key_passwords with the rotation.');\n}","typeGuard":null,"tryCatchPattern":"try {\n  await passbolt.rotateAccountRecoveryKey(payload);\n} catch (e) {\n  if (e.status === 400 && /Passwords are required for this change/.test(e.message)) {\n    // start the user-facing flow to collect re-encrypted passwords, then retry\n    return startReencryptionFlow();\n  }\n  throw e;\n}","preventionTips":["Before rotating, query the server for existing backups and plan the re-encryption collection workflow.","Never rotate the organization key on instances with users' backups without completing the re-encryption step.","Remember the password count must exactly match stored backups (assertPasswordsCount)."],"tags":["api","bad-request","key-rotation","account-recovery"],"backgroundTag":"missing-required-argument","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}