Skip to content

Pre-1.0: polyjose is not published yet, and its APIs may change before 1.0. Read the security policy before you rely on it.

Errors

This page describes polyjose 0.1.0 as designed. The library is in development and not published yet.

Every polyjose failure carries exactly one error code from a fixed set. The codes are the same strings on every platform, because every package reports the code the Rust core produced and never adds codes of its own. errorCodes() returns the full list in JavaScript, Kotlin and Swift, and Error::CODES holds it in Rust.

How each platform exposes the error code
PlatformError typeThe code
Rustpolyjose::ErrorError::code()
JavaScript (@polyjose/node, @polyjose/wasm)PolyjoseError, a subclass of Error whose name is “PolyjoseError”; the rejected Promise carries itPolyjoseError.code
KotlinPolyjoseException, a sealed class with one subclass per codePolyjoseException.code()
SwiftPolyjoseError, an enum with one case per codePolyjoseError.code()

In JavaScript, passing an argument of the wrong type (a number where a string belongs, for example) throws the host’s own TypeError before polyjose runs. That is a programming error, not one of the codes below, so err.code is always one of the codes whenever err instanceof PolyjoseError. Kotlin and Swift reject the same mistakes at compile time.

  • Decide what to do from the code. Messages are for people reading logs: their wording can change between versions, and they may name the claim or header involved.
  • Messages never contain input. Keys, passwords, tokens, payloads and plaintext stay out of every message and every error’s string form, on every platform, so an error is safe to log.
  • New codes may be added in later versions. Keep a default branch for codes you don’t handle.
Stable error codes, identical on every platform
CodeMeaningTypical causes
InvalidBase64A part that must be base64url is not valid unpadded base64url (RFC 4648 §5).
  • Padding (=), or the standard alphabet's + and /, in a token part or a JWK member
  • Whitespace or line breaks inside a token
  • A truncated or corrupted token
InvalidJsonInput that must be JSON is not strict I-JSON, or exceeds the parser's depth or size limits.
  • A JSON syntax error in a header, JWK, JWK Set or JSON serialization
  • Duplicate member names, which polyjose always rejects
  • Text that is not valid UTF-8
  • A JWT payload that is not a JSON object
InvalidHeaderA JOSE header is well-formed JSON but breaks a header rule.
  • alg missing, or a registered parameter with the wrong type
  • The same parameter in the protected and the unprotected header
  • crit outside the protected header, or malformed
  • b64 not listed in crit, or b64: false in a JWT
  • A JWT typ that the validation policy does not accept
  • Both or neither of the protected-header forms passed to a sign call
InvalidFormatThe serialization has the wrong shape, or the input is larger than the configured limit.
  • A compact JWS without exactly three parts, or a compact JWE without exactly five
  • A JSON serialization that does not have the shape the RFC defines
  • A detached payload supplied while the token also carries one, or missing when it does not
  • Input over the size limit (1 MiB by default) or a JSON JWS with more than 16 signatures
  • A JSON serialization passed to a JWT call, or a nested JWT passed to the signed-JWT verifier
UnsupportedAlgorithmThe algorithm, key type, curve, format or option is not implemented.
  • An alg or enc value polyjose does not know
  • RSA signing, or parsing an RSA private key
  • RSA-OAEP (SHA-1) or RSA1_5
  • A SHA-1 thumbprint
  • zip outside the Rust crate's deflate feature
  • An unknown format, hash, curve or serialization name passed as a string
AlgorithmNotAllowedThe algorithm is valid but not in the allow-list the caller passed.
  • The token's alg or enc is not in the allow-list (allowedAlgs or allowedEnc; allowed_algs or allowed_enc in Rust)
  • An empty allow-list, or one that names only none
  • A token with alg: none
  • In JavaScript, an allow-list that was left out
InvalidKeyThe key material is malformed or too weak for the algorithm.
  • An HMAC key shorter than the hash output
  • An RSA modulus outside 2048 to 8192 bits
  • An EC private key whose length is not the curve's field size
  • An Ed25519 public key of small order
  • A requested oct key length outside 16 to 512 bytes
KeyAlgorithmMismatchThe key is valid but does not belong to the algorithm or is not permitted for this use.
  • A key of the wrong family or curve, for example an oct key for RS256
  • A JWK whose alg differs from the header's (EdDSA and Ed25519 are different)
  • A JWK whose use is not sig, or whose key_ops lacks the operation
  • A public key passed to a sign call
InvalidSignatureThe signature or MAC does not verify with the given key.
  • The token was changed after signing
  • The wrong key, for example another issuer's
  • A signature of the wrong length, such as a DER-encoded ECDSA signature
DecryptionFailedDecryption failed, and the reason is deliberately not reported.
  • The wrong key, or a ciphertext, IV or tag that was changed
  • A PBES2 iteration count above the limit (10,000 by default)
  • Compressed content that is invalid or decompresses past the limit (250,000 bytes by default)
UnknownCriticalThe crit header names a parameter that polyjose does not understand.
  • Any crit entry other than b64 on a JWS
  • A JWE crit entry that the caller did not list as understood
TokenExpiredThe JWT's exp time has passed, after allowing for the leeway.
  • The token is older than its lifetime
  • The verifier's clock is ahead of the issuer's by more than the leeway (60 seconds by default)
TokenNotYetValidThe JWT's nbf time is still in the future, after allowing for the leeway.
  • The token is used before its start time
  • The verifier's clock is behind the issuer's by more than the leeway
InvalidClaimA JWT claim is missing, has the wrong type, or does not match the validation policy.
  • A required claim is missing, including exp, which is required by default
  • iss, sub or aud differs from what the policy expects
  • The token has an aud claim but the policy names no audience
  • A date claim that is a string, or outside the range a JavaScript number holds exactly
  • iat too old or in the future when a maximum age is set
  • A leeway above 600 seconds in the policy
NoMatchingKeyNo key in the JWK Set is a candidate for verifying this token.
  • No member has the token's kid
  • No member fits the token's alg: wrong key family or curve, or a JWK alg, use or key_ops that rules it out
  • More candidates than the limit (8 by default), for example a token without kid and a large set
RandomnessUnavailableThe operating system's secure random number generator could not be read.
  • A sandbox or runtime that blocks the platform's random source
  • Generating a key, encrypting or signing where no secure random source exists

DecryptionFailed is deliberately one code for every failure after key management, whatever the internal cause. Reporting which step failed would give an attacker an oracle to probe ciphertexts with.

A JWT can be wrong in several ways at once. polyjose runs its checks in a fixed order, and the first failing check decides the error, so the same token gives the same code on every platform.

  1. Size limits and the token’s shape: InvalidFormat. A JSON serialization is never a JWT, so it fails here too.
  2. base64url of every part: InvalidBase64.
  3. The header: InvalidJson if it isn’t valid JSON, then InvalidHeader for structural problems such as a missing alg.
  4. crit naming a parameter polyjose doesn’t understand: UnknownCritical.
  5. alg that isn’t a known JWS algorithm: UnsupportedAlgorithm.
  6. alg that isn’t in your allow-list: AlgorithmNotAllowed.
  7. JWT header rules (b64: false, cty: JWT on a signed JWT, the typ policy): InvalidHeader.
  8. Choosing the key. With a JWK Set, finding no candidate key, or more candidates than the limit, gives NoMatchingKey. With a single key, a key of the wrong family or use gives KeyAlgorithmMismatch, and weak key material gives InvalidKey.
  9. The signature: InvalidSignature. When a JWK Set offers several candidates and all fail, the furthest-reaching failure is reported, ranked InvalidSignature, then InvalidKey, then KeyAlgorithmMismatch.
  10. Parsing the claims: InvalidJson (including duplicate claim names), then InvalidClaim for a registered claim of the wrong type or a date out of range.
  11. Required claims, in the order iss, sub, aud, exp, nbf, iat, jti, then any extra names you require: InvalidClaim.
  12. exp: TokenExpired.
  13. nbf: TokenNotYetValid.
  14. iat, when you set a maximum age: InvalidClaim.
  15. iss: InvalidClaim.
  16. sub: InvalidClaim.
  17. aud, including a token that has an audience when you configured none: InvalidClaim.

Claims are read only after the signature verifies, so a tampered payload reports InvalidSignature, never a claim error.

For a nested JWT (signed, then encrypted), decryption runs first and reports its own errors, ending in DecryptionFailed. Then the outer header’s cty and typ are checked (InvalidHeader), then steps 1 to 17 run on the inner signed JWT, and finally any iss, sub or aud copied into the outer header must equal the inner claim (InvalidClaim).