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

ResourcesTagsAddController::addPost() validates the resourceId route parameter with CakePHP Validation::uuid() before doing any work; a malformed id raises BadRequestException. This is a first-line input-format check on the URL.

Solutions

  1. Pass the passbolt resource UUID (36-char, e.g. from GET /resources.json)
  2. Fix client URL construction so the id placeholder is actually substituted
  3. Validate the id client-side with a UUID regex/validator before calling the endpoint

Example fix

// before
await api.put(`/resources-tags/${resource.id}`);
// after
if (!/^[0-9a-f-]{36}$/i.test(resource.id)) throw new Error('Invalid resource UUID');
await api.put(`/resources-tags/${resource.id}`);
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(resourceId)) throw new Error('resourceId must be a UUID');

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 addResourceTags(id, tags); } catch (e) { if (e.status === 400 && /UUID/.test(e.message)) { /* fix client id source */ } else throw e; }

Prevention

When it happens

Trigger: PUT/POST to /resources-tags/<resourceId> (or the tags-add route) where resourceId is not a UUID: numeric ids, slugs, empty strings, or truncated identifiers.

Common situations: Client code passing a database auto-increment id or a name instead of the passbolt resource UUID; URL templating bug that leaves a placeholder in the path; hand-crafted curl tests.

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/512b7cb9f5a4e258. Report an issue: GitHub.

Appendix: source

Thrown at plugins/PassboltEe/Tags/src/Controller/Tags/ResourcesTagsAddController.php:64

    {
        parent::initialize();
        $this->Resources = $this->fetchTable('Resources');
        $this->Tags = $this->fetchTable('Passbolt/Tags.Tags');
    }

    /**
     * Add tags for a given resource.
     * Providing an empty list of tags delete all the personal tags
     *
     * @param string $resourceId The identifier of the resource to add a comment to
     * @throws \Cake\Http\Exception\BadRequestException
     * @throws \Cake\Http\Exception\NotFoundException
     * @return void
     */
    public function addPost(string $resourceId)
    {
        if (!Validation::uuid($resourceId)) {
            throw new BadRequestException(__('The resource identifier should be a valid UUID.'));
        }

        $uac = $this->User->getAccessControl();
        $data = $this->formatRequestData();
        $data = $this->validateRequestData($data, $uac);

        $options = ['contain' => ['all_tags' => 1, 'permission' => 1]];
        /** @var \App\Model\Entity\Resource|null $resource */
        $resource = $this->Resources->findView($uac->getId(), $resourceId, $options)->first();
        if (is_null($resource)) {
            throw new NotFoundException(__('The resource does not exist.'));
        }

        $tags = (new ResourcesTagsAddService())->add($uac, $resource, $data);
        $tags = (new MetadataTagsRenderService())->renderTags($tags);
        $this->success(__('The operation was successful.'), $tags);
    }

View on GitHub (pinned to 31c1bbc10f)