passbolt/passbolt_api · error · InternalErrorException
The user metadata session keys could not be deleted.
Error message
The user metadata session keys could not be deleted.
What it means
This InternalErrorException is thrown by deleteMetadataSessionKeys when deleteAll on the user's metadata session keys removes zero rows. The service treats a no-op delete as a failure so the caller knows the user's metadata session key cleanup did not happen.
Solutions
- Ensure the session key entities are freshly loaded and ids are present before calling delete()
- Check for concurrent deletions of the same user's metadata session keys
- Confirm the ids exist in the metadata session keys table at the time of deleteAll
- Return early (or treat as success) when the extracted id list is empty, mirroring the upstream guard
Example fix
// before
$metadataPrivateKeysIds = Hash::extract($metadataSessionKeys, '{n}.id');
$result = $metadataSessionKeysTable->deleteAll(['id IN' => $metadataPrivateKeysIds]);
if ($result <= 0) {
throw new InternalErrorException(__('The user metadata session keys could not be deleted.'));
}
// after
$metadataSessionKeysIds = Hash::extract($metadataSessionKeys, '{n}.id');
if (empty($metadataSessionKeysIds)) {
return;
}
$result = $metadataSessionKeysTable->deleteAll(['id IN' => $metadataSessionKeysIds]);
if ($result <= 0) {
throw new InternalErrorException(__('The user metadata session keys could not be deleted.'));
} Defensive patterns
Strategy: try-catch
Validate before calling
$ids = Hash::extract($metadataSessionKeys, '{n}.id');
if (!empty($ids) && TableRegistry::getTableLocator()->get('MetadataSessionKeys')->exists(['id IN' => $ids])) {
// safe to delete
} Type guard
function sessionKeysDeletable(array $entities): bool {
$ids = Hash::extract($entities, '{n}.id');
return !empty($ids) && array_reduce($ids, fn($ok, $id) => $ok && is_string($id) && Validation::uuid($id), true);
} Try / catch
try {
$service->delete($user);
} catch (InternalErrorException $e) {
// verify rows already deleted; treat as idempotent success if so
$this->log($e->getMessage(), 'error');
} Prevention
- Fetch session key entities and delete within one transaction
- Handle the already-deleted case as success to stay idempotent
- Avoid parallel jobs deleting the same user's session keys
- Note the copied variable name ($metadataPrivateKeysIds) in the source; use a clear name in your own code
When it happens
Trigger: Calling delete() on UserMetadataKeysDeleteService where $metadataSessionKeys entities were passed but their ids no longer match rows in the metadata session keys table (concurrent deletion, stale entities), or extracted ids are empty/invalid so the IN clause matches nothing.
Common situations: Two cleanup processes racing on the same user; entities fetched before another request already deleted the session keys; wrong table/entity passed so ids don't exist in that table.
Related errors
- The user metadata private keys could not be deleted.
- Could not delete the group
- Could not delete the user
- Could not save the rbacs, please try again later.
- Could not save the setting, please try again later.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/e4f4509381bbcd5a.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltCe/Metadata/src/Service/UserMetadataKeysDeleteService.php:95
private function deleteMetadataSessionKeys(string $userId): void
{
/** @var \Passbolt\Metadata\Model\Table\MetadataSessionKeysTable $metadataSessionKeysTable */
$metadataSessionKeysTable = $this->fetchTable('Passbolt/Metadata.MetadataSessionKeys');
$metadataSessionKeys = $metadataSessionKeysTable
->unhydratedFind()
->select(['id'])
->where(['user_id' => $userId])
->toArray();
if (empty($metadataSessionKeys)) {
// Nothing to delete
return;
}
$metadataPrivateKeysIds = Hash::extract($metadataSessionKeys, '{n}.id');
$result = $metadataSessionKeysTable->deleteAll(['id IN' => $metadataPrivateKeysIds]);
if ($result <= 0) {
throw new InternalErrorException(__('The user metadata session keys could not be deleted.'));
}
}
}
View on GitHub (pinned to 31c1bbc10f)