passbolt/passbolt_api · error · BadRequestException

The email can not be changed

Error message

The email can not be changed

What it means

In a SCIM PATCH `add` operation on a user, an operation targeting the `emails` attribute is unconditionally rejected: passbolt does not allow email addresses to be modified through SCIM because the email is the user's identity/login key. It throws a 400 BadRequestException with scimType `mutability`.

Solutions

  1. Remove the `emails` attribute from the IdP's SCIM attribute mapping so it is never sent in PATCH operations.
  2. Change the email through the passbolt admin UI or the `passbolt users` CLI command instead of SCIM.
  3. If the address must change, delete and re-create (re-provision) the user via SCIM.
  4. Configure the IdP to treat email as immutable for this provisioning app.

Example fix

// before (IdP mapping sends email updates)
{"Operations":[{"op":"add","path":"emails","value":[{"value":"new@mail.com"}]}]}
// after (map emails read-only; change it server-side instead)
// passbolt: bin/cake passbolt users change_user_email <user-id> new@mail.com
Defensive patterns

Strategy: validation

Validate before calling

if (String(op.path || '').startsWith('emails')) throw new Error('passbolt: emails cannot be changed via SCIM; use admin UI/CLI');

Try / catch

try { await scim.patchUser(id, ops); } catch (e) { if (e.status === 400 && e.scimType === 'mutability' && /email/i.test(e.message)) { ops = ops.filter(o => !String(o.path||'').startsWith('emails')); } else throw e; }

Prevention

When it happens

Trigger: PATCH /scim/v2/Users/<id> with op `add` and path/value containing `emails` (e.g. {"op":"add","path":"emails","value":[{"value":"new@mail.com"}]}).

Common situations: An IdP synchronization changes a user's email address and tries to propagate the change via SCIM; an admin tries to fix a typo'd email through SCIM instead of the passbolt UI/CLI; Azure AD provisioning maps the mail field as an updatable attribute.

Understand the failure class

Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.

Related errors


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/87d932f199c5cd5e. Report an issue: GitHub.

Appendix: source

Thrown at plugins/PassboltEe/Scim/src/Utility/Resource/UserScimResource.php:606

                                }
                                break;
                            case 'name.familyName':
                                if (empty($this->lastName)) {
                                    $userPatchData['profile']['last_name'] = $attributeValue;
                                }
                                break;
                            case 'active':
                                if ($this->active === null) {
                                    if (is_string($attributeValue)) {
                                        $value = in_array(strtolower($attributeValue), ['true', '1']);
                                    } else {
                                        $value = (bool)$attributeValue;
                                    }
                                    $userPatchData['disabled'] = $this->getDisabledValue($value);
                                }
                                break;
                            case 'emails':
                                throw new BadRequestException(
                                    'The email can not be changed',
                                    scimType: ScimException::SCIM_TYPE_MUTABILITY
                                );
                            default:
                                // ignore attributes not used in this application
                        }
                        break;
                    case Operation::TYPE_REPLACE:
                        switch ($attributeName) {
                            case 'externalId':
                                $scimEntryPatchData['external_identifier'] = $attributeValue;
                                break;
                            case 'userName':
                                $scimEntryPatchData['scim_name'] = $attributeValue;
                                break;
                            case 'name.givenName':
                                $userPatchData['profile']['first_name'] = $attributeValue;
                                break;

View on GitHub (pinned to 31c1bbc10f)