passbolt/passbolt_api · error · InvalidArgumentException
The resource identifier should be a valid UUID.
Error message
The resource identifier should be a valid UUID.
What it means
JwtTokenCreateService::createToken() validates that the $userId argument is a valid UUID with Validation::uuid() before signing a JWT. A non-UUID identifier throws this InvalidArgumentException, as documented in the method's @throws annotation.
Solutions
- Pass the user's id from the users table ( Cake\Routing uses UuidFactory::uuid() format), e.g. $user->id from a UsersTable lookup
- Validate the identifier before calling: \Cake\Validation\Validation::uuid($userId)
- If you only have a username/email, resolve it via UsersTable->find()->where(['username' => $email])->first() and use ->id
- Fix test fixtures/callers that pass literal placeholder strings
Example fix
// before
$jwt = $this->jwtService->createToken($user['username']);
// after
if (!\Cake\Validation\Validation::uuid($user['id'])) {
throw new \InvalidArgumentException('User id must be a UUID');
}
$jwt = $this->jwtService->createToken($user['id']); Defensive patterns
Strategy: validation
Validate before calling
use Cake\Validation\Validation;
if (!Validation::uuid($userId)) { throw new \InvalidArgumentException('$userId must be a UUID'); } Type guard
function isUuid(mixed $id): bool {
return is_string($id) && \Cake\Validation\Validation::uuid($id);
} Try / catch
try { $token = $service->createToken($userId); } catch (\InvalidArgumentException $e) { // resolve the real user id from UsersTable before retrying } Prevention
- Always pass the users.id UUID, never username/email
- Validate identifiers with Validation::uuid() at API boundaries
- Fetch the user entity first and use ->id
When it happens
Trigger: Calling createToken($userId, $expiration) with an empty string, an integer cast to string, an email address, or any string that is not a RFC 4122 UUID (e.g. '1', 'me', 'abc').
Common situations: Passing a user's username/email instead of id; using a null/empty value after a failed user lookup; calling createToken from custom code or tests with placeholder identifiers.
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
- Invalid user ID format.
- Invalid verify token format.
- The refresh token should be a valid UUID.
- The user id is missing or invalid.
- " " is not a valid group id for filter .
AI-assisted analysis of passbolt/passbolt_api@31c1bbc10f (2026-09-17).
Data as JSON: /api/errors/a6638ca78348dc2d.
Report an issue: GitHub.
Appendix: source
Thrown at plugins/PassboltCe/JwtAuthentication/src/Service/AccessToken/JwtTokenCreateService.php:47
{
public const JWT_SECRET_KEY_PATH = self::JWT_CONFIG_DIR . 'jwt.key';
public const JWT_ALG = 'RS256';
public const JWT_KEY_LENGTH = 4096;
public const JWT_EXPIRY_CONFIG_KEY = 'passbolt.auth.token.access_token.expiry';
protected string $keyPath = self::JWT_SECRET_KEY_PATH;
/**
* @param string $userId The id of the user successfully logging in.
* @param string|null $expiration The validity duration of the token in words (optional).
* @return string
* @throws \InvalidArgumentException if the userId is not a valid Uuid
* @throws \Passbolt\JwtAuthentication\Error\Exception\AccessToken\InvalidJwtKeyPairException if the JWT secret key is not readable.
*/
public function createToken(string $userId, ?string $expiration = null): string
{
if (!Validation::uuid($userId)) {
throw new InvalidArgumentException(__('The resource identifier should be a valid UUID.'));
}
$privateKey = $this->readKeyFileContent();
$payload = [
'iss' => Router::url('/', true),
'sub' => $userId,
'exp' => $this->createExpiryDate($expiration),
];
return JWT::encode($payload, $privateKey, self::JWT_ALG);
}
/**
* Create a UNIX time from a time expressed in words.
* This should return an integer.
*
* @param string|null $expirationPeriod Expiration period in words.
* @return int Unix timeView on GitHub (pinned to 31c1bbc10f)