passbolt/passbolt_api · critical · Cake\Http\Exception\InternalErrorException

Could not save the user data. Please try again later.

Error message

Could not save the user data. Please try again later.

What it means

InternalErrorException thrown when UsersTable::save() returns false while persisting the edited user (with 'checkrules' => false since rules were already checked). This indicates an unexpected persistence failure — database error, avatar filesystem write failure (the filesystem adapter option is passed to save), or a save() event/behavior aborting — not a client input problem.

Solutions

  1. Check server logs for the underlying CakePHP/database error accompanying the 500.
  2. Verify database connectivity and that migrations are up to date (`ddev refresh` / passbolt migration status).
  3. If avatars are enabled, check the avatar filesystem adapter's storage path permissions and free space.
  4. Disable recently added plugins that hook into user save events and retry to isolate the aborting listener.

Example fix

// server-side: ensure storage is writable before retrying the request
// before
sudo chown www-data:www-data /var/www/html/webroot/img/public/Avatar
// after
sudo chown -R www-data:www-data /var/www/html/webroot/img/public/Avatar && sudo -u www-data test -w /var/www/html/webroot/img/public/Avatar
Defensive patterns

Strategy: retry

Try / catch

try { await api.editUser(id, data); } catch (e) { if (e.code === 500 && /Could not save the user data/.test(e.message)) { await sleep(1000); return retryWithBackoff(() => api.editUser(id, data), 3); } throw e; }

Prevention

When it happens

Trigger: PUT /users/{id}.json where validation and rules passed but save() fails: DB unreachable/locked, avatar storage adapter cannot write, an attached model event (e.g. from a plugin) returns false, or a race deleted the row mid-request.

Common situations: Database down or credentials changed; disk full or wrong permissions on the avatar storage path; a plugin's Users event listener aborts saves; migrations out of date so a column is missing.

Related errors


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

Appendix: source

Thrown at src/Controller/Users/UsersEditController.php:110

            throw new ValidationException(__('Could not validate user data.'), $user, $this->Users);
        }
        $this->Users->checkRules($user);
        if ($user->getErrors()) {
            throw new ValidationException(__('Could not validate user data.'), $user, $this->Users);
        }
        $isBeingDisabled = $wasDisabledNull && !is_null($user->disabled);

        // Used when sending after update event
        // We need entity's dirty state to know which column values has been changed.
        $userEntityWithDirtyState = clone $user;

        // Save
        $saveOptions = [
            'checkrules' => false,
            AvatarsTable::FILESYSTEM_ADAPTER_OPTION => $filesystemAdapter,
        ];
        if (!$this->Users->save($user, $saveOptions)) {
            throw new InternalErrorException('Could not save the user data. Please try again later.');
        }

        if ($isBeingDisabled) {
            /** @var \App\Model\Table\SecretsTable $secretsTable */
            $secretsTable = $this->fetchTable('Secrets');
            $secretToExpire = $secretsTable->findByUserId($id)
                ->select(['id', 'user_id', 'resource_id'])->all()->toArray();
            $resourcesExpireResourcesService->expireResourcesForSecrets($secretToExpire);
        }

        // Get the updated version (ex. Role needs to be fetched again if role_id changed)
        try {
            /** @var \App\Model\Entity\User $user */
            $user = $this->Users->findView($id, $this->User->role())->firstOrFail();
        } catch (Exception $exception) {
            $msg = __('Could not find the user data after save. Maybe it has been deleted in the meantime.');
            throw new InternalErrorException($msg, 500, $exception);
        }

View on GitHub (pinned to 31c1bbc10f)