passbolt/passbolt_api · error · Exception

The version is invalid.

Error message

The version is invalid.

What it means

assertVersion ensures the challenge's version field equals the PROTOCOL_VERSION constant supported by this authenticator. A mismatch throws a plain Exception which verifyChallenge wraps into 'The challenge is invalid. Validation Failed.' — the protocol version negotiated by the client is not supported.

Solutions

  1. Align the client SDK version with the server's passbolt version and the PROTOCOL_VERSION it expects
  2. Set the challenge's version field exactly to the protocol constant (case-sensitive string)
  3. Check passbolt release notes for JWT protocol version changes after upgrading the server
  4. Inspect server logs for the logged challenge to see which version value was sent

Example fix

// before
const challenge = { version: 'v1', ... };
// after (server expects PROTOCOL_VERSION 'v2')
const challenge = { version: 'v2', ... };
Defensive patterns

Strategy: fallback

Validate before calling

if (challenge.version !== SUPPORTED_PROTOCOL_VERSION) challenge.version = SUPPORTED_PROTOCOL_VERSION; // or rebuild challenge

Type guard

function hasSupportedVersion(c) { return typeof c.version === 'string' && c.version === 'v2'; }

Try / catch

try { await login(challenge); } catch (e) { if (/Validation Failed/.test(e.message) && challenge.version !== 'v2') { await login({ ...challenge, version: 'v2' }); } }

Prevention

When it happens

Trigger: verifyChallenge on POST /auth/jwt/login where the decrypted challenge has version missing, non-string, or a value different from self::PROTOCOL_VERSION (e.g. client built for an older protocol 'v1' vs 'v2').

Common situations: Client SDK and passbolt server versions out of sync after a server upgrade; hand-rolled login scripts hardcoding an old version string; typo like 'V1' (case-sensitive comparison).

Related errors


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

Appendix: source

Thrown at plugins/PassboltCe/JwtAuthentication/src/Authenticator/GpgJwtAuthenticator.php:430

    /**
     * @param mixed $armoredChallenge challenge
     * @throws \InvalidArgumentException if armored challenge is invalid
     * @return void
     */
    public function assertArmoredChallenge(mixed $armoredChallenge): void
    {
        $this->assertGpgMessageIsValid($this->gpg, $armoredChallenge, __('The user challenge is missing or invalid.'));
    }

    /**
     * @param mixed $version version
     * @throws \Exception if version is not supported
     * @return void
     */
    public function assertVersion(mixed $version): void
    {
        if (!isset($version) || !is_string($version) || $version !== self::PROTOCOL_VERSION) {
            throw new Exception(__('The version is invalid.'));
        }
    }

    /**
     * Assert domain
     *
     * @param mixed $domain domain
     * @return void
     * @throws \Passbolt\JwtAuthentication\Error\Exception\Challenge\InvalidDomainException if domain is invalid
     */
    public function assertDomain(mixed $domain): void
    {
        if (!isset($domain) || !is_string($domain)) {
            throw new InvalidDomainException(__('The domain is invalid.'));
        }

        if (rtrim($domain, '/') !== rtrim(Router::url('/', true), '/')) {
            $expect = rtrim(Router::url('/', true));

View on GitHub (pinned to 31c1bbc10f)