passbolt/passbolt_api · warning · NotFoundException
The metadata key has already been deleted.
Error message
The metadata key has already been deleted.
What it means
Thrown when the targeted metadata key already has its 'deleted' flag set, i.e. a previous delete already succeeded. The service rejects the redundant delete with NotFoundException (HTTP 404) to keep delete idempotent-unfriendly and explicit.
Solutions
- Check the key's deleted flag (or re-fetch the key) before calling delete
- Treat 404 with this message as 'already done' and skip rather than fail
- Avoid retrying delete blindly on timeout — first query the key state
Example fix
// before
await deleteMetadataKey(id);
await deleteMetadataKey(id); // second call throws
// after
if (!isKeyDeleted(id)) {
await deleteMetadataKey(id);
} Defensive patterns
Strategy: validation
Validate before calling
const key = await getMetadataKey(id); if (key.deleted) return; // already deleted, skip
Try / catch
try {
await deleteMetadataKey(id);
} catch (e) {
if (e.status === 404 && /already been deleted/.test(e.message)) return;
throw e;
} Prevention
- Make delete steps idempotent in scripts by checking deleted flag first
- Don't blindly retry delete on timeout — verify state first
When it happens
Trigger: DELETE /metadata/keys/<id> executed twice; a retry after a timed-out-but-successful first delete; another admin deleted the key between the caller's fetch and the delete call.
Common situations: Automated scripts or CI pipelines that re-run a migration step without checking current key state; double-click / duplicate form submission in tooling built on the API.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- The metadata key does not exist or has been deleted.
- The metadata key does not exist or has been deleted.
- The metadata key fingerprint is invalid.
- The record does not exist.
- AssociatedRecordExists
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/634a1ca1ea389e6e.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltCe/Metadata/src/Service/MetadataKey/MetadataKeyDeleteService.php:66
$uac->assertIsAdmin();
if (!Validation::uuid($id)) {
throw new BadRequestException(__('The metadata key ID should be a valid UUID.'));
}
$metadataKeysTable = $this->fetchTable('Passbolt/Metadata.MetadataKeys');
// Assert the key exist
try {
/** @var \Passbolt\Metadata\Model\Entity\MetadataKey $metadataKey */
$metadataKey = $metadataKeysTable->get($id);
} catch (RecordNotFoundException $exception) { // @phpstan-ignore-line
throw new NotFoundException(__('The metadata key does not exist or has been deleted.'), 404, $exception);
}
// Assert the key is not already deleted
if ($metadataKey->isDeleted()) {
throw new NotFoundException(__('The metadata key has already been deleted.'));
}
// Assert they key was previously marked as expired
if (!$metadataKey->isExpired()) {
throw new BadRequestException(__('The metadata key should be marked as expired first.'));
}
// Assert the key is not used by folders, resources, tags, etc.
if ((new MetadataKeyAssertUsageService())->isKeyInUse($metadataKey->get('id'))) {
$msg = __('The metadata key is still in use, migrate the remaining items to the new key first.');
throw new BadRequestException($msg);
}
// Patch the key deleted field with the current time
$options['accessibleFields'] = ['deleted' => true, 'modified_by' => true];
$patch = ['deleted' => DateTime::now(), 'modified_by' => $uac->getId()];
$metadataKey = $metadataKeysTable->patchEntity($metadataKey, $patch, $options);
if ($metadataKey->getErrors()) {View on GitHub (pinned to 31c1bbc10f)