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
- Retry the delete after checking the database is reachable and the row is not locked (check SHOW PROCESSLIST / slow query log).
- Verify DB write connectivity and that the groups table is not read-only or out of disk space.
- Check logs for the underlying SQL/PDOException to identify constraint or lock causes.
- 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
- Keep the database healthy (monitor locks, disk, replication lag).
- Avoid concurrent writes to the same group row.
- Retry idempotent deletes on transient DB failures.
- Watch error logs for repeated save() failures.
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
- Could not delete the user
- Could not save the group, try again later.
- The user metadata private keys could not be deleted.
- The user metadata session keys could not be deleted.
- 500
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)