passbolt/passbolt_api · error · BadRequestException

The resource identifier should be a valid UUID.

Error message

The resource identifier should be a valid UUID.

What it means

Passbolt throws this 400 when the DELETE /resources/<id> path parameter is not a valid UUID. ResourcesDeleteController::delete validates with Validation::uuid() before attempting to load the resource.

Solutions

  1. Fetch the resource UUID from GET /resources.json and retry DELETE with it
  2. Add a UUID format check before issuing the DELETE
  3. Fix client code to pass the entity's id property, not name/slug
  4. Check for URL-encoding or truncation issues if the id appears correct in source

Example fix

// before
await api.del(`/resources/${row.name}.json`);
// after
if (!/^[0-9a-f-]{36}$/i.test(row.id)) throw new Error('invalid resource id');
await api.del(`/resources/${row.id}.json`);
Defensive patterns

Strategy: validation

Validate before calling

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);
if (!isUuid(resourceId)) throw new Error('delete requires a resource UUID');

Type guard

const deletable = (r) => isUuid(r?.id) && r.personal === false ? r.id : null;

Try / catch

if (!isUuid(id)) { id = await lookupIdFromIndex(name); }

Prevention

When it happens

Trigger: DELETE /resources/<id>.json with numeric/slug/empty/truncated id segments from malformed client calls or legacy id schemes.

Common situations: Scripts iterating over a list where a name or index was passed instead of the id; string slicing bugs; older integrations built for numeric ids; double URL-encoding corrupting the UUID.

Understand the failure class

Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.

Related errors


AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17). Data as JSON: /api/errors/85d3ab143b3d1a05. Report an issue: GitHub.

Appendix: source

Thrown at src/Controller/Resources/ResourcesDeleteController.php:81

    /**
     * Resource Delete action
     *
     * @param string $id The identifier of the resource to delete.
     * @throws \Cake\Http\Exception\NotFoundException If the resource does not exist.
     * @throws \Cake\Http\Exception\NotFoundException If the resource is soft deleted.
     * @throws \Cake\Http\Exception\NotFoundException If the user does not have access to the resource.
     * @throws \Cake\Http\Exception\ForbiddenException If the user does not have the permission to delete the resource.
     * @throws \Cake\Http\Exception\BadRequestException If the resource id is not a valid uuid.
     * @throws \Cake\Http\Exception\InternalErrorException if the resource could not be saved for other reasons
     * @return void
     */
    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]]];

View on GitHub (pinned to 31c1bbc10f)