passbolt/passbolt_api · error · NotFoundException

The group does not exist.

Error message

The group does not exist.

What it means

Passbolt throws this 404 when updating a group whose id is syntactically a valid UUID but does not match any row in the groups table. GroupsUpdateController::assertRequestParameter checks Groups->exists(['id' => $id]) before doing anything else and fails fast so no permission checks run on a non-existent group.

Solutions

  1. List groups via GET /groups.json and confirm the target id exists, then retry with a valid id
  2. Check the groups table (SELECT id FROM groups WHERE id='<id>') to verify presence in the connected database
  3. If the group was deleted, recreate it or update the calling client to use the current group id
  4. Ensure the client is pointed at the intended environment (the id may exist elsewhere)

Example fix

// before
await fetch(`/groups/${staleGroupId}.json`, {method: 'PUT', ...});
// after
const groups = await fetch('/groups.json').then(r => r.json());
const group = groups.body.find(g => g.name === 'Marketing');
await fetch(`/groups/${group.id}.json`, {method: 'PUT', ...});
Defensive patterns

Strategy: validation

Validate before calling

const UUID_RE = /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i;
if (!UUID_RE.test(groupId)) throw new Error('invalid group id');
const exists = (await fetch('/groups.json').then(r=>r.json())).body.some(g=>g.id===groupId);
if (!exists) throw new Error(`group ${groupId} not found on this instance`);

Type guard

const isUuid = (v) => typeof v === 'string' && /^[0-9a-f]{8}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{4}-[0-9a-f]{12}$/i.test(v);

Try / catch

try {
  await passbolt.group.update(groupId, payload);
} catch (e) {
  if (e.status === 404 && /group does not exist/i.test(e.message)) {
    // refresh id from /groups.json and retry once
  } else throw e;
}

Prevention

When it happens

Trigger: Calling PUT /groups/<id> (or its dry-run variant) with an id that is a valid UUID but was never created, was already deleted, or belongs to another environment's database.

Common situations: Stale URLs/bookmarks after a group was deleted; copying an id from a different install or test fixture; a client caching group ids after a purge/migration; hardcoding ids from documentation examples.

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/79d33a719a56c4dc. Report an issue: GitHub.

Appendix: source

Thrown at src/Controller/Groups/GroupsUpdateController.php:131

    /**
     * Assert the request parameter.
     *
     * @param \App\Utility\UserAccessControl $uac The operator
     * @param string $id group uuid
     * @return void
     * @throws \Cake\Http\Exception\ForbiddenException If the operator is not a group manager or an admin
     * @throws \Cake\Http\Exception\BadRequestException if the group uuid id invalid
     */
    protected function assertRequestParameter(UserAccessControl $uac, string $id)
    {
        if (!Validation::uuid($id)) {
            throw new BadRequestException(__('The group id is not valid.'));
        }

        $exists = $this->Groups->exists(['id' => $id]);
        if (!$exists) {
            throw new NotFoundException(__('The group does not exist.'));
        }

        // If the user is not manager of the group nor admin
        $isGroupManager = $this->GroupsUsers->isManager($uac->getId(), $id);
        $isAdmin = $uac->isAdmin();
        if (!$isGroupManager && !$isAdmin) {
            throw new ForbiddenException(__('You are not authorized to access that location.'));
        }
    }

    /**
     * Get and format the request data.
     *
     * @return array
     */
    protected function _formatRequestData()
    {
        $data = $this->request->getData();

View on GitHub (pinned to 31c1bbc10f)