passbolt/passbolt_api · error · InvalidArgumentException

Invalid user ID format.

Error message

Invalid user ID format.

What it means

Thrown by populatedMetadataUserKeyId when the supplied $userId is not a valid UUID (Validation::uuid fails). The trait uses the user ID to populate metadata user key ids in request data, and refuses to proceed with a malformed identifier.

Solutions

  1. Pass a valid UUID string (36 chars, canonical 8-4-4-4-12 hex format) as $userId
  2. Validate the id client-side before calling, e.g. with the same UUID regex used by CakePHP Validation::uuid()
  3. Check routing/config so the user id is correctly bound to the action parameter
  4. If the id comes from an entity, use $entity->id rather than a manually supplied value

Example fix

// before
$service->populatedMetadataUserKeyId($userId, $data); // $userId = 'admin'
// after
if (!Validation::uuid($userId)) {
    throw new BadRequestException(__('A valid user identifier is required.'));
}
$service->populatedMetadataUserKeyId($userId, $data);
Defensive patterns

Strategy: validation

Validate before calling

use Cake\Validation\Validation;
if (!is_string($userId) || !Validation::uuid($userId)) {
    throw new BadRequestException(__('Invalid user ID.'));
}

Type guard

function isUuidString(mixed $value): bool {
    return is_string($value) && (bool)preg_match('/^[a-f0-9]{8}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{4}-[a-f0-9]{12}$/i', $value);
}

Try / catch

try {
    $data = $trait->populatedMetadataUserKeyId($userId, $data);
} catch (InvalidArgumentException $e) {
    return $this->response->withStatus(400)->withStringBody(json_encode(['error' => 'Invalid user ID format']));
}

Prevention

When it happens

Trigger: Calling a controller/service action using this trait with a user ID that is null, empty, non-UUID string, or an integer id instead of a UUID string; routing parameters bound incorrectly so the id placeholder holds something else.

Common situations: Passing a username/email or numeric legacy id where a UUID is expected; URL building mistakes that omit the id segment; tests or CLI code constructing request data with dummy ids.

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/81553310d827fb5e. Report an issue: GitHub.

Appendix: source

Thrown at plugins/PassboltCe/Metadata/src/Utility/MetadataPopulateUserKeyIdTrait.php:39

use Cake\Validation\Validation;
use InvalidArgumentException;
use Passbolt\Metadata\Model\Dto\MetadataDto;
use Passbolt\Metadata\Model\Entity\MetadataKey;

trait MetadataPopulateUserKeyIdTrait
{
    /**
     * Update sent v5 entities request data when METADATA_KEY_TYPE TYPE_USER_KEY is set to null
     * By dynamically inserting the current gpgkey_id in its place
     *
     * @param string $userId uuid
     * @param mixed $data request data
     * @return array
     */
    public function populatedMetadataUserKeyId(string $userId, mixed $data): array
    {
        if (!Validation::uuid($userId)) {
            throw new InvalidArgumentException(__('Invalid user ID format.'));
        }
        if (!isset($data) || !is_array($data)) {
            throw new BadRequestException(__('The data is required.'));
        }
        if (
            isset($data[MetadataDto::METADATA])
            && isset($data[MetadataDto::METADATA_KEY_TYPE])
            && is_string($data[MetadataDto::METADATA_KEY_TYPE])
            && $data[MetadataDto::METADATA_KEY_TYPE] === MetadataKey::TYPE_USER_KEY
            && !isset($data[MetadataDto::METADATA_KEY_ID])
        ) {
            $keyTable = TableRegistry::getTableLocator()->get('Gpgkeys');
            $key = $keyTable->find('current', userId: $userId)->firstOrFail();
            $id = $key->get('id');
            if (Validation::uuid($id)) {
                $data[MetadataDto::METADATA_KEY_ID] = $id;
            }
        }

View on GitHub (pinned to 31c1bbc10f)