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

  1. Make deletion idempotent: treat 404 on DELETE as success in cleanup/sync scripts
  2. Refresh the resource list and verify the id still exists before deleting
  3. Check the resource_types table has a matching type row for the resource
  4. 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

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


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)