jwtk/jjwt · error · DecodingException

Illegal ${name} character: '${c}'

Error message

Illegal ${name} character: '${c}'

What it means

Base64.ctoi maps each character to its 6-bit value using the Base64 alphabet; if a character is outside the alphabet (index lookup returns -1) JJWT throws DecodingException with 'Illegal <Base64/Base64Url> character'. This guards the decode path in Base64.decode/Base64Url against malformed input. getName() reports whether the Base64 or Base64Url variant rejected the character.

Source

Thrown at api/src/main/java/io/jsonwebtoken/io/Base64.java:221

            }
            // Add the bytes
            dArr[d++] = (byte) (i >> 16);
            if (d < len) {
                dArr[d++] = (byte) (i >> 8);
                if (d < len) {
                    dArr[d++] = (byte) i;
                }
            }
        }
        return dArr;
    }
    */

    private int ctoi(char c) {
        int i = c > IALPHABET_MAX_INDEX ? -1 : IALPHABET[c];
        if (i < 0) {
            String msg = "Illegal " + getName() + " character: '" + c + "'";
            throw new DecodingException(msg);
        }
        return i;
    }

    /**
     * Decodes a BASE64-encoded {@code CharSequence} that is known to be reasonably well formatted. The preconditions
     * are:<br>
     * + The sequence must have a line length of 76 chars OR no line separators at all (one line).<br>
     * + Line separator must be "\r\n", as specified in RFC 2045
     * + The sequence must not contain illegal characters within the encoded string<br>
     * + The sequence CAN have illegal characters at the beginning and end, those will be dealt with appropriately.<br>
     *
     * @param seq The source sequence. Length 0 will return an empty array. <code>null</code> will throw an exception.
     * @return The decoded array of bytes. May be of length 0.
     * @throws DecodingException on illegal input
     */
    byte[] decodeFast(CharSequence seq) throws DecodingException {

View on GitHub (pinned to fb71496164)

Solutions

  1. Inspect the offending character shown in the message and remove/fix it in the input (whitespace, quotes, wrapping are the usual culprits)
  2. Use the correct decoder for your data: Jwts.parser() for full JWTs, Base64Url for URL-safe segments, Base64 only for standard-base64 data
  3. Normalize input before decoding: strip whitespace/newlines and re-encode if the source used the wrong alphabet
  4. Parse the JWT with its dedicated API instead of manually base64-decoding segments so segment boundaries are handled for you

Example fix

// before
byte[] sig = Base64Url.decode(token.replace("\n", "").getBytes()); // still may contain '+'
// DecodingException: Illegal Base64Url character: '+'

// after
Jws<Claims> jws = Jwts.parserBuilder().build().parseClaimsJws(token.trim());
Defensive patterns

Strategy: validation

Validate before calling

// Java: strip whitespace and validate base64url alphabet before decoding
boolean isBase64Url(String s) {
    return s != null && s.matches("[A-Za-z0-9_-]*");
}
String sanitize(String s) {
    return s == null ? null : s.replaceAll("\\s+", "").replace("\"", "");
}

Type guard

boolean isDecodableSegment(String segment) {
    return segment != null && !segment.isEmpty()
        && segment.matches("[A-Za-z0-9_-]+="); // base64url chars only
}

Try / catch

try {
    byte[] decoded = Base64Url.decode(segment);
} catch (DecodingException e) {
    // segment contained an illegal character; reject or re-encode input
}

Prevention

When it happens

Trigger: Calling Jwts.parser() on a token whose signature or payload segment contains characters not valid for the variant being decoded — e.g. '+' or '/' in a Base64Url segment, whitespace/newlines inside a token pasted from an email or PDF, quotes or trailing period, or decoding arbitrary text with JJWT's Base64/Base64Url utility directly.

Common situations: JWT copied with line breaks or smart quotes; token signed/encoded with standard Base64 but decoded as Base64Url (or vice versa); storing tokens in systems that wrap lines; hand-rolling JWT parsing and passing the whole token (with dots) where only a segment should be decoded.

Understand the failure class

Background: "Invalid ... format", "must be in format X", "does not look like a ..." — invalid argument format errors across CLI tools and libraries — this error's family across 17 libraries.

Related errors


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