passbolt/passbolt_api · error · Exception

User could not be updated.

Error message

User could not be updated.

What it means

This generic Exception is thrown by UserSyncAction::updateUser when the Users table save() call returns false but the entity carries no validation errors, so the sync action cannot report a precise cause. It is a last-resort fallback in the LDAP directory-sync user-update flow: the persistence layer failed for a reason outside field validation (e.g. database error, event listener aborting the save). Because the message carries no entity or SQL detail, debugging requires inspecting server logs.

Solutions

  1. Check passbolt error logs (logs/error.log) for the underlying database or event-listener error at the time of the sync run.
  2. Re-run the sync with debug enabled or inspect $user->getErrors() and the save() return path in UserSyncAction to identify why save() failed without validation errors.
  3. Verify the users table schema matches migrations: run `ddev refresh` / `passbolt migrate` to ensure no stale constraints.
  4. Disable third-party plugins hooking into Users model events to rule out a listener aborting the save, then re-run sync.

Example fix

// before: silent fallback hides the cause
throw new Exception('User could not be updated.');
// after: surface the underlying save failure
$err = $this->Users->getConnection()->errorInfo ?? null;
throw new Exception('User could not be updated. Save failed' . ($err ? ': ' . json_encode($err) : '.') . ' User id: ' . $existingUser->id);
Defensive patterns

Strategy: try-catch

Validate before calling

if (!$user->getErrors() && !$this->Users->save($user)) {
    error_log('User save failed for id ' . $existingUser->id . ' errors: ' . json_encode($user->getErrors()));
}

Try / catch

try {
    $syncService->updateUser($user, $entry);
} catch (ValidationException $e) {
    // entity-level issues
} catch (Exception $e) {
    if ($e->getMessage() === 'User could not be updated.') {
        // inspect server logs / retry save manually to find root cause
    }
}

Prevention

When it happens

Trigger: Running `passbolt directory_sync` sync where an existing user matched from the LDAP directory has changed attributes (e.g. full name) and $this->Users->save($user) returns false with $user->hasErrors() === false. Typical concrete causes: a beforeSave/afterSave rule failing silently, a database constraint violation, or a model event listener calling stopPropagation/aborting the save without setting entity errors.

Common situations: AD/LDAP sync updating a user whose data passes entity validation but breaks at the database level (e.g. NULL in a non-null column introduced by a plugin hook); corrupted users table row; custom plugin event listeners on Model.beforeSave that block saves; MySQL/Postgres constraint mismatch after a partial migration.

Related errors


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

Appendix: source

Thrown at plugins/PassboltEe/DirectorySync/src/Actions/UserSyncAction.php:144

    /**
     * Update user
     *
     * @param \App\Model\Entity\User $existingUser User
     * @param array $data data
     * @return void
     */
    private function updateUser(User $existingUser, array $data): void
    {
        try {
            $user = $this->Users->editEntity($existingUser, $data, new UserAccessControl(Role::ADMIN));
            $result = $this->Users->save($user, ['checkrules' => false]);

            if (!$result) {
                if ($user->hasErrors()) {
                    $msg = __('Could not validate user data.');
                    throw new ValidationException($msg, $user, $this->Users);
                }
                throw new Exception('User could not be updated.');
            }
            // Send report.
            $this->addReportItem(new ActionReport(
                __(
                    'The user {0} full name has been successfully updated to {1} {2}.',
                    $existingUser->username,
                    $user->profile->first_name,
                    $user->profile->last_name
                ),
                Alias::MODEL_USERS,
                Alias::ACTION_UPDATE,
                Alias::STATUS_SUCCESS,
                $user
            ));
        } catch (Exception $exception) {
            $error = new SyncError($existingUser, $exception);
            $this->addReportItem(new ActionReport(
                __(

View on GitHub (pinned to 31c1bbc10f)