passbolt/passbolt_api · error · InvalidJwtKeyPairException

The JWT public key could not be read or is not valid.

Error message

The JWT public key could not be read or is not valid.

What it means

validateKeyPair() first checks is_readable() on the JWT public key path; if unreadable it throws this message. It also throws the identical message when getSecretKeySize() returns 0, i.e. the private key could not be read/parsed either. Both mean the installed JWT key pair files are missing, unreadable, or corrupt.

Solutions

  1. Generate the key pair: sudo -u www-data bin/cake passbolt create jwt_keys (or the pro equivalent)
  2. Verify both files exist and are non-empty: ls -la config/jwt/; head -1 config/jwt/jwt.public.key
  3. Fix permissions so the runtime user can read them: chown www-data:www-data config/jwt/*; chmod 640 config/jwt/*
  4. If files are empty/corrupt, delete them and regenerate the pair

Example fix

// before
# empty jwt.public.key after failed generation
// after
rm config/jwt/jwt.public.key config/jwt/jwt.private.key
sudo -u www-data bin/cake passbolt create jwt_keys
Defensive patterns

Strategy: validation

Validate before calling

$pub = Configure::read('passbolt.jwt.publicKeyPath') ?? CONFIG . 'jwt' . DS . 'jwt.public.key';
if (!is_readable($pub) || filesize($pub) === 0) { // regenerate key pair before calling validateKeyPair
}

Type guard

if (!is_string($pub) || !is_file($pub) || !is_readable($pub)) { /* trigger regeneration */ }

Try / catch

try { $service->validateKeyPair(); } catch (InvalidJwtKeyPairException $e) { // run bin/cake passbolt create jwt_keys as runtime user }

Prevention

When it happens

Trigger: validateKeyPair() is called (via execute) and either config/jwt/jwt.public.key is not readable by the current user, or its content/secret key size is 0 (empty or unparseable key files).

Common situations: JWT keys never generated (`passbolt create jwt_keys` skipped after install/upgrade); files lost during deployment because config/jwt was not persisted; empty files from a partially failed generation; wrong user permissions after container restart.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


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

Appendix: source

Thrown at plugins/PassboltCe/JwtAuthentication/src/Service/AccessToken/JwtKeyPairService.php:117

            throw new InvalidJwtKeyPairException($e->getMessage());
        }
    }

    /**
     * Validate the key pair validity as defined by the public and secret services.
     *
     * @param string|null $uuid Uuid for testing aim
     * @return object
     * @throws \Passbolt\JwtAuthentication\Error\Exception\AccessToken\InvalidJwtKeyPairException
     */
    public function validateKeyPair(?string $uuid = null): object
    {
        // Minimal size of the private key
        $minSecretKeySize = JwtTokenCreateService::JWT_KEY_LENGTH;
        $uuid = $uuid ?? UuidFactory::uuid();
        try {
            if (!is_readable($this->publicService->getKeyPath())) {
                throw new Exception(__('The JWT public key could not be read or is not valid.'));
            }
            $publicKey = file_get_contents($this->publicService->getKeyPath());
            $secretKeySize = $this->publicService->getSecretKeySize();

            if ($secretKeySize === 0) {
                throw new Exception(__('The JWT public key could not be read or is not valid.'));
            }

            if ($secretKeySize < $minSecretKeySize) {
                throw new Exception(__(
                    'The JWT private key should be at least {0} bytes long.',
                    $this->secretService::JWT_KEY_LENGTH
                ));
            }

            $jwt = $this->secretService->createToken($uuid, '2 seconds');

            return JWT::decode($jwt, new Key($publicKey, $this->secretService::JWT_ALG));

View on GitHub (pinned to 31c1bbc10f)