passbolt/passbolt_api · error · Cake\Http\Exception\InternalErrorException

Invalid UserControl user id.

Error message

Invalid UserControl user id.

What it means

UserAccessControl::__construct() validates its optional $userId argument with Cake's Validation::uuid(). If a userId is provided but is not a valid UUID string, it throws InternalErrorException('Invalid UserControl user id.'), since access-control context must be anchored to a valid user identifier.

Solutions

  1. Validate/normalize the id with Validation::uuid($userId) before constructing UserAccessControl, and reject invalid requests earlier (400).
  2. Ensure the value passed really is the user UUID, not a numeric/legacy id — fetch the UUID from the users table if needed.
  3. Return a proper client error instead of surfacing the internal 500: validate input at the controller layer before building the access control object.

Example fix

// before
$uac = new UserAccessControl($roleName, $this->request->getQuery('user_id')); // 500 if invalid
// after
$userId = $this->request->getQuery('user_id');
if ($userId !== null && !Validation::uuid($userId)) {
    throw new BadRequestException('Invalid user id.');
}
$uac = new UserAccessControl($roleName, $userId);
Defensive patterns

Strategy: validation

Validate before calling

if ($userId !== null && !\Cake\Validation\Validation::uuid($userId)) {
    throw new BadRequestException('A valid user id (UUID) is required.');
}
$uac = new UserAccessControl($roleName, $userId);

Type guard

function isValidUserId(?string $userId): bool {
    return $userId === null || \Cake\Validation\Validation::uuid($userId);
}

Try / catch

try {
    $uac = new UserAccessControl($roleName, $userId);
} catch (\Cake\Http\Exception\InternalErrorException $e) {
    if (str_contains($e->getMessage(), 'Invalid UserControl user id')) {
        throw new BadRequestException('Invalid user id supplied.');
    }
    throw $e;
}

Prevention

When it happens

Trigger: Constructing `new UserAccessControl($roleName, $userId)` where $userId is a non-UUID string (e.g. an integer id, garbage, or a concatenated value) — commonly when the id comes from an unvalidated request parameter or a legacy column.

Common situations: Passing route/query parameters straight into UserAccessControl without prior UUID validation; tests using fake ids like '1' or 'test'; refactored code passing username where userId was expected.

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

Appendix: source

Thrown at src/Utility/UserAccessControl.php:60

     */
    private string $roleName;

    /**
     * @var string|null
     */
    private ?string $username = null;

    /**
     * UserAccessControl constructor.
     *
     * @param string $roleName The role name
     * @param string|null $userId the user uuid
     * @param string|null $username the user email
     */
    public function __construct(string $roleName, ?string $userId = null, ?string $username = null)
    {
        if (isset($userId) && !Validation::uuid($userId)) {
            throw new InternalErrorException('Invalid UserControl user id.');
        }
        if (isset($username) && !EmailValidationRule::check($username)) {
            throw new InternalErrorException('Invalid UserControl username.');
        }
        $this->userId = $userId;
        $this->roleName = $roleName;
        $this->username = $username;
    }

    /**
     * Get the user id
     *
     * @return string|null
     */
    public function getId(): ?string
    {
        return $this->userId;
    }

View on GitHub (pinned to 31c1bbc10f)