{"record":{"id":"5f9bf15adbaaf4fa","repo":"passbolt/passbolt_api","slug":"the-user-metadata-private-keys-could-not-be-deleted","errorCode":null,"errorMessage":"The user metadata private keys could not be deleted.","messagePattern":"The user metadata private keys could not be deleted\\.","errorType":"http","errorClass":"InternalErrorException","httpStatus":500,"severity":"error","filePath":"plugins/PassboltCe/Metadata/src/Service/UserMetadataKeysDeleteService.php","lineNumber":68,"sourceCode":"    private function deleteMetadataPrivateKeys(string $userId): void\n    {\n        /** @var \\Passbolt\\Metadata\\Model\\Table\\MetadataPrivateKeysTable $metadataPrivateKeysTable */\n        $metadataPrivateKeysTable = $this->fetchTable('Passbolt/Metadata.MetadataPrivateKeys');\n\n        $metadataPrivateKeys = $metadataPrivateKeysTable\n            ->unhydratedFind()\n            ->select(['id'])\n            ->where(['user_id' => $userId])\n            ->toArray();\n        if (empty($metadataPrivateKeys)) {\n            // Nothing to delete\n            return;\n        }\n\n        $metadataPrivateKeysIds = Hash::extract($metadataPrivateKeys, '{n}.id');\n        $result = $metadataPrivateKeysTable->deleteAll(['id IN' => $metadataPrivateKeysIds]);\n        if ($result <= 0) {\n            throw new InternalErrorException(__('The user metadata private keys could not be deleted.'));\n        }\n    }\n\n    /**\n     * @param string $userId User identifier.\n     * @return void\n     * @throws \\Cake\\Http\\Exception\\InternalErrorException If data is not deleted.\n     */\n    private function deleteMetadataSessionKeys(string $userId): void\n    {\n        /** @var \\Passbolt\\Metadata\\Model\\Table\\MetadataSessionKeysTable $metadataSessionKeysTable */\n        $metadataSessionKeysTable = $this->fetchTable('Passbolt/Metadata.MetadataSessionKeys');\n\n        $metadataSessionKeys = $metadataSessionKeysTable\n            ->unhydratedFind()\n            ->select(['id'])\n            ->where(['user_id' => $userId])\n            ->toArray();","sourceCodeStart":50,"sourceCodeEnd":86,"githubUrl":"https://github.com/passbolt/passbolt_api/blob/31c1bbc10f32808a607fa9bd81891e898779c0bc/plugins/PassboltCe/Metadata/src/Service/UserMetadataKeysDeleteService.php#L50-L86","documentation":"This InternalErrorException is thrown by deleteMetadataPrivateKeys when a deleteAll over the user's metadata private key records affects zero rows. It signals that the deletion of metadata private keys (required when removing a user's metadata key data) silently failed, so the service aborts the delete operation with an internal error rather than leaving inconsistent state.","triggerScenarios":"Calling delete() on UserMetadataKeysDeleteService for a user whose $metadataPrivateKeys entities were fetched but whose ids no longer exist in the metadata_private_keys table at deleteAll time (e.g. concurrent deletion, transaction isolation mismatch), or passing entities with missing/invalid ids so Hash::extract yields ids matching nothing.","commonSituations":"Race conditions where two requests delete the same user's metadata keys; orphaned/soft-deleted rows filtered out by deleteAll; ids extracted from stale entities after a prior partial delete.","solutions":["Verify the metadata private key entities passed in were freshly fetched and their id fields are populated before calling delete()","Check for concurrent deletions (jobs, other requests) targeting the same user's metadata private keys","Log the extracted $metadataPrivateKeysIds and confirm rows with those ids exist in the metadata_private_keys table","Wrap the whole delete() flow in a transaction so fetch and delete are consistent"],"exampleFix":"// before\n$result = $metadataPrivateKeysTable->deleteAll(['id IN' => $metadataPrivateKeysIds]);\nif ($result <= 0) {\n    throw new InternalErrorException(__('The user metadata private keys could not be deleted.'));\n}\n// after\n$metadataPrivateKeysIds = Hash::extract($metadataPrivateKeys, '{n}.id');\nif (empty($metadataPrivateKeysIds)) {\n    return; // nothing to delete is not a failure\n}\n$result = $metadataPrivateKeysTable->deleteAll(['id IN' => $metadataPrivateKeysIds]);\nif ($result <= 0) {\n    throw new InternalErrorException(__('The user metadata private keys could not be deleted.'));\n}","handlingStrategy":"try-catch","validationCode":"$ids = Hash::extract($metadataPrivateKeys, '{n}.id');\nif (empty($ids) || !TableRegistry::getTableLocator()->get('MetadataPrivateKeys')->exists(['id IN' => $ids])) {\n    // skip or reconcile before deleting\n}","typeGuard":"function hasDeletableIds(array $entities): bool {\n    $ids = Hash::extract($entities, '{n}.id');\n    return !empty($ids) && array_reduce($ids, fn($ok, $id) => $ok && is_string($id) && Validation::uuid($id), true);\n}","tryCatchPattern":"try {\n    $service->delete($user);\n} catch (InternalErrorException $e) {\n    // inspect metadata_private_keys table state, retry with fresh entities\n    $this->log($e->getMessage(), 'error');\n}","preventionTips":["Always pass freshly fetched entities with populated ids","Run the delete inside a transaction with the fetch","Check idempotency: treat zero-row deletes for already-deleted keys as success","Log extracted ids for post-mortem on races"],"tags":["database","delete","internal-error","cakephp"],"backgroundTag":"database-write-failed","analyzedSha":"31c1bbc10f32808a607fa9bd81891e898779c0bc","analyzedAt":"2026-09-17T00:04:38.960Z","contentChangedAt":"2026-09-17T00:04:38.960Z","schemaVersion":2},"datasetVersion":"2026-09-21T04:17:39.646Z"}