passbolt/passbolt_api · error · NotFoundException
The resource does not exist.
Error message
The resource does not exist.
What it means
Passbolt throws this 404 when DELETE /resources/<id> uses a valid UUID but the resource lookup (find()->where(Resources.id)->firstOrFail()) throws RecordNotFoundException. The resource simply isn't in the database (or its resource type row is missing via the contain).
Solutions
- Make deletion idempotent: treat 404 on DELETE as success in cleanup/sync scripts
- Refresh the resource list and verify the id still exists before deleting
- Check the resource_types table has a matching type row for the resource
- Reconcile environments if the id came from another instance
Example fix
// before
await api.del(`/resources/${id}.json`); // throws on 404
// after
try {
await api.del(`/resources/${id}.json`);
} catch (e) {
if (e.status !== 404) throw e; // already deleted: ok
} Defensive patterns
Strategy: try-catch
Validate before calling
const stillThere = (await fetch('/resources.json').then(r=>r.json())).body.some(r=>r.id===resourceId);
if (!stillThere) return { deleted: true, alreadyGone: true }; Type guard
null
Try / catch
try {
await deleteResource(id);
} catch (e) {
if (e.status === 404) return { deleted: true }; // idempotent delete
throw e;
} Prevention
- Make deletes idempotent — another client may have deleted first
- Refresh resource lists before batch deletions
- Never replay recorded delete operations against a restored database without existence checks
When it happens
Trigger: Deleting an already-deleted resource (double delete / race between two clients); UUID from a different environment; a resource whose ResourceTypes row is absent so the contain leaves it unjoinable.
Common situations: Optimistic-concurrency bugs where two tabs delete the same resource; restore/migration scripts replaying deletions; test fixtures referencing production ids; broken resource_types data after manual DB edits.
Understand the failure class
Background: "Not found" and "does not exist" errors: why "Task not found", "No such folder", and "Can't find" fire when a lookup comes back empty — this error's family across 14 libraries.
Related errors
- The resource does not exist.
- The comment does not exist.
- The favorite does not exist.
- The group does not exist.
- The group does not exist.
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/8ac3dc312768079a.
Report an issue: GitHub.
Appendix: source
Thrown at src/Controller/Resources/ResourcesDeleteController.php:93
public function delete(string $id): void
{
$this->assertJson();
// Check request sanity
if (!Validation::uuid($id)) {
throw new BadRequestException(__('The resource identifier should be a valid UUID.'));
}
// Retrieve the resource to delete.
try {
/** @var \App\Model\Entity\Resource $resource */
$resource = $this->Resources->find()
->contain(['ResourceTypes'])
->where(['Resources.id' => $id])
->firstOrFail();
$originalResource = clone $resource;
} catch (RecordNotFoundException $e) {
throw new NotFoundException(__('The resource does not exist.'));
}
// Get the list of users who have access to the resource
// useful to do now to notify users later, since it wont be possible to after delete
// The logged in user will not be notified.
$options = ['contain' => ['role'], 'filter' => ['has-access' => [$resource->id]]];
$users = $this->Users
->findIndex(Role::USER, $options)
->find('locale')
->where(['Users.id !=' => $this->User->id()])
->all();
// Update the entity to delete=1, clear uri/desc/username and drop associated permissions
if (!$this->Resources->softDelete($this->User->id(), $resource)) {
$this->_handleDeleteError($resource);
throw new InternalErrorException('Could not delete the resource. Please try again later.');
}
View on GitHub (pinned to 31c1bbc10f)