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

Could not delete the group

Error message

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

What it means

Thrown by GroupsTable::softDelete() when the ORM fails to persist the 'deleted = true' flag on the group entity. The permissions rows were already removed, so the group is left in an inconsistent state and the caller receives a 500 Internal Error instead of the group disappearing. It signals a database-level save failure (connection issue, lock, constraint), not a validation problem, since validation and rules checks are disabled for this save.

Solutions

  1. Retry the delete after checking the database is reachable and the row is not locked (check SHOW PROCESSLIST / slow query log).
  2. Verify DB write connectivity and that the groups table is not read-only or out of disk space.
  3. Check logs for the underlying SQL/PDOException to identify constraint or lock causes.
  4. Because permissions were already deleted before the save, manually clean up (restore permissions or hard-delete the orphaned group) if the failure persists.

Example fix

// caller before
catch (InternalErrorException $e) { /* generic 500 */ }
// after
catch (InternalErrorException $e) {
    // verify DB health, then retry via a new request, e.g. with backoff
    $this->Groups->softDelete($group);
}
Defensive patterns

Strategy: retry

Validate before calling

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

Type guard

if (!is_string($groupId) || !Cake\Validation\Validation::uuid($groupId)) { throw new InvalidArgumentException(); }

Try / catch

try { $this->Groups->softDelete($group); } catch (\Cake\Http\Exception\InternalErrorException $e) { // check DB health, retry with backoff or surface 500 }

Prevention

When it happens

Trigger: Calling the group delete API (DELETE /groups/{id}.json) or GroupsTable::softDelete() when $this->save($group, ['checkRules' => false, 'validate' => false]) returns false — e.g. database connection lost, deadlock/lock wait timeout on the groups row, or a trigger/constraint rejecting the update.

Common situations: Database under heavy load or during failover; MySQL lock wait timeouts when concurrent operations touch the same group; misconfigured or read-only DB replica receiving writes; disk-full database server.

Related errors


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

Appendix: source

Thrown at src/Model/Table/GroupsTable.php:378

                $foldersRelationsTable
                    ->deleteAll(['foreign_id IN' => $foldersIds]);
                $foldersRelationsTable
                    ->updateAll(['folder_parent_id' => null], ['folder_parent_id IN ' => $foldersIds]);
            }
        }

        // Delete all group memberships
        $this->GroupsUsers->deleteAll(['group_id' => $group->id]);

        // Delete all permissions
        // Delete all the secrets that lost permissions in the process
        $this->Permissions->deleteAll(['aro_foreign_key' => $group->id]);

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

        return $entitiesChanges;
    }

    /**
     * Delete all groups records with no members(groups_users).
     *
     * @param bool $dryRun false
     * @return int Number of affected records
     */
    public function cleanupWithNoMembers(bool $dryRun = false): int
    {
        $query = $this->selectQuery()
            ->select(['id'])
            ->leftJoinWith('GroupsUsers')
            ->whereNull('GroupsUsers.id')
            ->where([$this->aliasField('deleted') => 0]);

View on GitHub (pinned to 31c1bbc10f)