passbolt/passbolt_api · critical · InternalErrorException

Could not delete the user

Error message

Could not delete the user {0}, please try again later.

What it means

Thrown by UsersTable::softDelete() when persisting 'deleted = true' on the user entity fails via save() with checkRules disabled. The user is not removed logically, and the API returns a 500. Because validation is bypassed, the failure indicates a database or persistence-layer problem rather than bad input.

Solutions

  1. Retry the deletion after confirming the database is reachable and no locks exist on the users row.
  2. Inspect error logs for the underlying SQL exception to identify the root cause.
  3. Verify users table integrity and disk space; ensure writes are not hitting a read-only replica.
  4. If the failure happened after partial cleanup (associated data may already be deleted), reconcile state manually or re-run the deletion flow.

Example fix

// caller before
catch (InternalErrorException $e) { /* swallow */ }
// after
catch (InternalErrorException $e) {
    // check DB health, then retry user deletion with backoff
}
Defensive patterns

Strategy: retry

Validate before calling

if (!$this->Users->exists(['id' => $userId, 'deleted' => false])) { return; }

Type guard

if (!Cake\Validation\Validation::uuid($userId)) { throw new InvalidArgumentException('Invalid user id.'); }

Try / catch

try { $this->Users->softDelete($user); } catch (\Cake\Http\Exception\InternalErrorException $e) { // inspect DB, retry with backoff or reconcile state }

Prevention

When it happens

Trigger: DELETE /users/{id}.json (user deletion flow) or direct softDelete() call when the UPDATE on the users row fails — connection drop, row lock/timeout, trigger/constraint rejection, or DB read-only state.

Common situations: Concurrent operations locking the users row (e.g. an active session update); MySQL lock wait timeout; DB failover; disk-full; GPG/auth token side-tables with blocking triggers in customized installs.

Related errors


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

Appendix: source

Thrown at src/Model/Table/UsersTable.php:562

                'foreign_key' => $user->id,
                'foreign_model' => ScimEntry::FOREIGN_MODEL_USERS,
            ]);
        }

        // Delete all tags
        if (Configure::read('passbolt.plugins.tags.enabled')) {
            $ResourcesTags = TableRegistry::getTableLocator()->get('Passbolt/Tags.ResourcesTags');
            $ResourcesTags->deleteAll(['user_id' => $user->id]);
            /** @var \Passbolt\Tags\Model\Table\TagsTable $Tags */
            $Tags = TableRegistry::getTableLocator()->get('Passbolt/Tags.Tags');
            $Tags->deleteAllUnusedTags();
        }

        // Mark user as deleted
        $user->deleted = true;
        if (!$this->save($user, ['checkRules' => false])) {
            $msg = __('Could not delete the user {0}, please try again later.', $user->username);
            throw new InternalErrorException($msg);
        }

        return $entitiesChanges;
    }

    /**
     * Register a user
     *
     * @param array $data register data
     * @param \App\Utility\UserAccessControl|null $control who is requesting the registration
     * @throws \Cake\Http\Exception\InternalErrorException if there was an issue during the save
     * @throws \App\Error\Exception\ValidationException if the user data do not validate
     * @return \App\Model\Entity\User entity
     */
    public function register(array $data, ?UserAccessControl $control = null): User
    {
        // if role id is empty make it a user
        // Only admins are allowed to set the role

View on GitHub (pinned to 31c1bbc10f)