passbolt/passbolt_api · error · CustomValidationException

The role could not be updated.

Error message

The role could not be updated.

What it means

RolesUpdateService::update throws this CustomValidationException when the edited role entity fails model validation during saveOrFail (PersistenceFailedException). The entity errors are embedded so the API consumer sees exactly which field was rejected.

Solutions

  1. Check the `errors` object in the 400 response for the failing field.
  2. Ensure the new role name is unique among existing roles (GET /roles).
  3. Send a non-empty name within the length limit.
  4. Retry PUT /roles/{id} with a corrected payload.

Example fix

// before
PUT /roles/<id> {"name": "editor"} // duplicate -> 400
// after
PUT /roles/<id> {"name": "editor-v2"} -> 200
Defensive patterns

Strategy: validation

Validate before calling

const existing = await api.listRoles();
function validRename(roleId, newName, existing) {
  return typeof newName === 'string' && newName.trim().length > 0
    && newName.length <= 255
    && !existing.some(r => r.id !== roleId && r.name === newName);
}
// abort PUT if validRename(...) is false

Type guard

function isRenamePayload(v) {
  return typeof v === 'object' && v !== null && typeof v.name === 'string' && v.name.trim().length > 0;
}

Try / catch

try {
  await api.updateRole(roleId, { name });
} catch (e) {
  if (e.status === 400 && e.body?.errors?.name) {
    // duplicate or invalid name: prompt user for a different one
  } else throw e;
}

Prevention

When it happens

Trigger: PUT /roles/{id} with a new name that violates Roles table rules: empty name, name longer than 255 characters, or uniqueness rule violation (renaming a role to an existing role's name).

Common situations: Renaming a role to a name that already exists, submitting a blank name from a UI that allowed empty input, or overly long names pasted from external sources.

Related errors


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

Appendix: source

Thrown at src/Service/Roles/RolesUpdateService.php:68

     * @param \App\Utility\UserAccessControl $uac UAC object.
     * @param string $roleId Role identifier to update.
     * @param array $data Data to save.
     * @return \App\Model\Entity\Role
     */
    public function update(UserAccessControl $uac, string $roleId, array $data): Role
    {
        $uac->assertIsAdmin();

        $role = $this->getRole($roleId);
        $oldName = $role->name;
        $role = $this->patchRoleEntity($role, $data, $uac);

        try {
            $result = $this->Roles->saveOrFail($role);
        } catch (PersistenceFailedException $e) { // @phpstan-ignore-line
            $errors = $e->getEntity()->getErrors();

            throw new CustomValidationException(
                __('The role could not be updated.'),
                $errors
            );
        } catch (Exception $e) {
            throw new InternalErrorException(__('Could not update the role, please try again later.'), null, $e);
        }

        $this->dispatchEvent(self::AFTER_ROLE_UPDATE_SUCCESS_EVENT_NAME, [
            'uac' => $uac,
            'role' => $result,
            'oldName' => $oldName,
        ]);

        return $result;
    }

    /**
     * @param string $roleId Role identifier to get.

View on GitHub (pinned to 31c1bbc10f)