jwtk/jjwt · error · java.lang.IllegalArgumentException

Strict decoding: Last encoded character (before the…

Error message

Strict decoding: Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible encoding. Expected the discarded bits from the character to be zero.

What it means

Error "Strict decoding: Last encoded character (before the paddings if any) is a valid base 64 alphabet but not a possible encoding. Expected the discarded bits from the character to be zero." thrown in jwtk/jjwt.

Solutions

  1. Re-encode the input with a standards-compliant Base64 encoder so the unused low bits of the final character are zero (strict/RFC 4648 canonical encoding)
  2. Regenerate or re-fetch the token/data, since non-canonical padding bits usually indicate corruption or a non-conformant encoder
  3. If lenient decoding of legacy data is required, decode without strict mode rather than altering the strict decoder
Defensive patterns

Strategy: try-catch

When it happens

Trigger: Thrown at impl/src/main/java/io/jsonwebtoken/impl/io/Base64Codec.java:774 when the library encounters an invalid state.

Common situations: See trigger scenarios.


AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09). Data as JSON: /api/errors/6c28f0dce7355669. Report an issue: GitHub.

Appendix: source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/io/Base64Codec.java:774

    public boolean isUrlSafe() {
        return this.encodeTable == URL_SAFE_ENCODE_TABLE;
    }

    /**
     * Validates whether decoding the final trailing character is possible in the context
     * of the set of possible base 64 values.
     * <p>
     * The character is valid if the lower bits within the provided mask are zero. This
     * is used to test the final trailing base-64 digit is zero in the bits that will be discarded.
     * </p>
     *
     * @param emptyBitsMask The mask of the lower bits that should be empty
     * @param context       the context to be used
     * @throws IllegalArgumentException if the bits being checked contain any non-zero value
     */
    private void validateCharacter(final int emptyBitsMask, final Context context) {
        if (isStrictDecoding() && (context.ibitWorkArea & emptyBitsMask) != 0) {
            throw new IllegalArgumentException(
                    "Strict decoding: Last encoded character (before the paddings if any) is a valid " +
                            "base 64 alphabet but not a possible encoding. " +
                            "Expected the discarded bits from the character to be zero.");
        }
    }

    /**
     * Validates whether decoding allows an entire final trailing character that cannot be
     * used for a complete byte.
     *
     * @throws IllegalArgumentException if strict decoding is enabled
     */
    private void validateTrailingCharacter() {
        if (isStrictDecoding()) {
            throw new IllegalArgumentException(
                    "Strict decoding: Last encoded character (before the paddings if any) is a valid " +
                            "base 64 alphabet but not a possible encoding. " +
                            "Decoding requires at least two trailing 6-bit characters to create bytes.");

View on GitHub (pinned to fb71496164)