{"record":{"id":"b7f5fa111a2645a1","repo":"passbolt/passbolt_api","slug":"invalid-request-new-key-or-passwords-are-not-required","errorCode":null,"errorMessage":"Invalid request. New key or passwords are not required.","messagePattern":"Invalid request\\. New key or passwords are not required\\.","errorType":"http","errorClass":"Cake\\Http\\Exception\\BadRequestException","httpStatus":400,"severity":"error","filePath":"plugins/PassboltEe/AccountRecovery/src/Service/AccountRecoveryOrganizationPolicies/AccountRecoveryOrganizationPolicySetService.php","lineNumber":81,"sourceCode":"        // if disabled => enabled\n        if ($this->isEnabling()) {\n            // if public key is not provided\n            if (!$isNewKeyProvided) {\n                throw new BadRequestException(__('Invalid request. An organization recovery public key is required.'));\n            }\n            // if key revocation or passwords provided\n            if ($isRevokedKeyProvided || $isPrivateKeyPasswordsProvided) {\n                throw new BadRequestException(__('Invalid request. Revoked key or passwords are not required.'));\n            }\n\n            return $this->enablePolicy($uac, $newPolicy);\n        }\n\n        // if enabled => disabled\n        if ($this->isDisabling()) {\n            // if new key or passwords provided\n            if ($isNewKeyProvided || $isPrivateKeyPasswordsProvided) {\n                throw new BadRequestException(__('Invalid request. New key or passwords are not required.'));\n            }\n\n            // save new disabled policy, disable previous key and delete backups if any\n            return $this->disablePolicy($uac);\n        }\n\n        // if enabled => enabled\n        // e.g it's policy change like mandatory => opt-in\n        // and/or a possible key rotation\n        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 */","sourceCodeStart":63,"sourceCodeEnd":99,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltEe/AccountRecovery/src/Service/AccountRecoveryOrganizationPolicies/AccountRecoveryOrganizationPolicySetService.php#L63-L99","documentation":"This BadRequestException is thrown when an administrator tries to disable the account recovery organization policy while including a new organization public key or private key passwords in the request payload. Disabling recovery must be a clean operation: the current key is revoked, backups are truncated, and no replacement key material is accepted. The service rejects the request as invalid rather than silently ignoring the extra data.","triggerScenarios":"POST/PUT to the account recovery organization settings endpoint with policy set to 'disabled' while the payload also contains 'account_recovery_organization_public_key' or 'account_recovery_private_key_passwords'. Occurs in AccountRecoveryOrganizationPolicySetService::set() at the isDisabling() branch.","commonSituations":"A client script or admin UI re-sends the previous payload (which included the public key) but changes only the policy field to 'disabled'; automation tooling that always includes the full settings object; API consumers copying the 'enable' request shape for the 'disable' call.","solutions":["Remove account_recovery_organization_public_key and account_recovery_private_key_passwords from the request payload when setting policy to 'disabled'.","Send only the policy field: {\"policy\": \"disabled\"} (plus any required revocation fields handled elsewhere).","If a client UI/library adds these fields automatically, update it or its serializer to strip key data on disable.","Review the API docs for the disable flow: keys are revoked server-side, replacement data is never accepted."],"exampleFix":"// before (disable with leftover key data)\nawait passbolt.updateAccountRecoveryOrganizationPolicy({\n  policy: 'disabled',\n  account_recovery_organization_public_key: armoredKey\n});\n\n// after (disable with minimal payload)\nawait passbolt.updateAccountRecoveryOrganizationPolicy({\n  policy: 'disabled'\n});","handlingStrategy":"validation","validationCode":"const isDisabling = payload.policy === 'disabled';\nif (isDisabling && (payload.account_recovery_organization_public_key || payload.account_recovery_private_key_passwords)) {\n  throw new Error('Do not send new key or passwords when disabling account recovery.');\n}","typeGuard":"function isDisablePayload(p) {\n  return p.policy === 'disabled' &&\n    p.account_recovery_organization_public_key === undefined &&\n    p.account_recovery_private_key_passwords === undefined;\n}","tryCatchPattern":"try {\n  await passbolt.setAccountRecoveryOrganizationPolicy(payload);\n} catch (e) {\n  if (e.status === 400 && /New key or passwords are not required/.test(e.message)) {\n    delete payload.account_recovery_organization_public_key;\n    delete payload.account_recovery_private_key_passwords;\n    return retry(payload);\n  }\n  throw e;\n}","preventionTips":["Build the disable payload from scratch instead of mutating an enable/rotate payload.","Strip key-related fields in a serializer whenever policy is 'disabled'.","Check the GET organization-policies endpoint first to know the current state before composing the request."],"tags":["api","bad-request","account-recovery","validation"],"backgroundTag":"invalid-argument-value","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"}