passbolt/passbolt_api · error · NotFoundException
The metadata key has already been deleted.
Error message
The metadata key has already been deleted.
What it means
MetadataKeyUpdateService::update() throws a NotFoundException when the target metadata key's deleted flag is set. Once a key is soft-deleted it is immutable, so any further update (e.g. marking it expired) is refused. The 404-style message intentionally hides the deleted state distinction from unauthorized callers.
Solutions
- Check the key's deleted flag (GET /metadata/keys) before attempting any update
- If the key must exist, recreate it via the metadata key create endpoint instead of updating the deleted one
- Remove the duplicate/stale update request from the client workflow
- If deletion was a mistake, restore the key's deleted field via an allowed flow rather than update()
Example fix
// before
$service->update($uac, $keyId, $dto); // throws if key deleted
// after
$key = $keysTable->get($keyId);
if (!$key->isDeleted()) {
$service->update($uac, $keyId, $dto);
} Defensive patterns
Strategy: validation
Validate before calling
$key = $metadataKeysTable->find()->where(['id' => $keyId])->first();
if ($key === null || $key->isDeleted()) {
return; // skip update for deleted keys
} Type guard
$isUpdatable = fn (MetadataKey $k): bool => !$k->isDeleted() && !$k->isExpired();
Try / catch
try {
$service->update($uac, $keyId, $dto);
} catch (NotFoundException $e) {
// key deleted or missing: refresh local cache of metadata keys
} Prevention
- Always GET the key state before updating
- Refresh client-side key cache after any deletion
- Treat deleted metadata keys as immutable in client logic
When it happens
Trigger: Calling PUT /metadata/keys/{id} (update) on a metadata key whose entity returns isDeleted() === true, e.g. re-expiring or re-arming an already deleted key.
Common situations: Client cached a stale key id after another admin deleted the key; replaying an update request twice; UI not refreshed after a deletion.
Understand the failure class
Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.
Related errors
- Missing metadata private key.
- The metadata private key does not exist or has been deleted.
- AssociatedRecordExists
- Could not find comments for the requested model.
- Could not save the metadata key, please try again later.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/13e9eec61d7b8e5b.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltCe/Metadata/src/Service/MetadataKey/MetadataKeyUpdateService.php:72
$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 fingerprint is the same
if ($metadataKey->fingerprint !== $dto->fingerprint) {
throw new NotFoundException(__('The metadata key fingerprint is invalid.'));
}
// Assert the key is not already deleted
if ($metadataKey->isDeleted()) {
throw new NotFoundException(__('The metadata key has already been deleted.'));
}
// Assert they key was not previously marked as expired
if ($metadataKey->isExpired()) {
throw new BadRequestException(__('The metadata key is already marked as expired.'));
}
// Patch the key deleted field with the current time
$options = [
'accessibleFields' => [
'fingerprint' => true, 'armored_key' => true, 'expired' => true, 'modified_by' => true,
],
'validate' => 'update',
];
$patch = [
'fingerprint' => $dto->fingerprint,
'armored_key' => $dto->armoredKey,
'expired' => $dto->expired,View on GitHub (pinned to 31c1bbc10f)