jwtk/jjwt · error · DecodingException
Unable to decode input: ${e.getMessage()}
Error message
Unable to decode input: ${e.getMessage()} What it means
ExceptionPropagatingDecoder is JJWT's wrapper that adapts any Decoder implementation into JJWT's Decoder contract. Its decode method rethrows DecodingException as-is, but if the delegate decoder throws any other checked/unchecked Exception (e.g. IOException from a custom decoder), it is wrapped in a DecodingException with 'Unable to decode input: ...'. This normalizes decoder failures for callers while keeping the original cause.
Source
Thrown at api/src/main/java/io/jsonwebtoken/io/ExceptionPropagatingDecoder.java:57
/**
* Decode the specified encoded data, delegating to the wrapped Decoder, wrapping any
* non-{@link DecodingException} as a {@code DecodingException}.
*
* @param t the encoded data
* @return the decoded data
* @throws DecodingException if there is an unexpected problem during decoding.
*/
@Override
public R decode(T t) throws DecodingException {
Assert.notNull(t, "Decode argument cannot be null.");
try {
return decoder.decode(t);
} catch (DecodingException e) {
throw e; //propagate
} catch (Exception e) {
String msg = "Unable to decode input: " + e.getMessage();
throw new DecodingException(msg, e);
}
}
}
View on GitHub (pinned to fb71496164)
Solutions
- Check getCause() to find the real failure from the delegate decoder and fix that root cause
- Verify the input type matches what the decoder expects (String vs byte[]) and that the data is valid for the decoder's format
- If you wrote a custom Decoder, catch your internal failures and throw DecodingException from decode() so they propagate unchanged
- Ensure consistent JJWT api/impl/runtime versions on the classpath to avoid mismatched decoder behavior
Example fix
// before
public byte[] doDecode(String s) throws IOException {
return Files.readAllBytes(Paths.get(s)); // raw IOException -> Unable to decode input
}
// after
public byte[] doDecode(String s) {
try {
return Files.readAllBytes(Paths.get(s));
} catch (IOException e) {
throw new DecodingException("Unable to read input", e);
}
} Defensive patterns
Strategy: try-catch
Validate before calling
// Java: check input type and nullness before handing to a decoder
boolean isDecodableInput(Object in) {
if (in instanceof String s) return !s.isEmpty();
if (in instanceof byte[] b) return b.length > 0;
return false;
} Type guard
boolean isStringOrBytes(Object in) {
return in instanceof String || in instanceof byte[];
} Try / catch
try {
byte[] out = decoder.decode(input);
} catch (DecodingException e) {
Throwable root = e.getCause();
// null cause = delegate threw DecodingException; non-null = wrapped unexpected failure
} Prevention
- Throw DecodingException (not raw IOException/RuntimeException) from custom Decoder implementations
- Confirm the input type matches the decoder contract (String vs byte[]) before decoding
- Keep JJWT api/impl versions aligned to avoid cross-version decoder mismatches
- Null/empty-check inputs before decode calls
- Inspect getCause() whenever you see the 'Unable to decode input' wrapper
When it happens
Trigger: Using a custom Decoder (e.g. via io.jsonwebtoken.io.Decoders or supplied to an Encoder/Decoder-based component) whose decode() throws a non-DecodingException — a raw IOException, RuntimeException, or third-party library exception — during any JJWT operation that decodes input (token parsing, Base64 decoding of keys, etc.).
Common situations: A custom Decoder reading from a stream/file that fails with IOException; a delegate decoder throwing NullPointerException on malformed input instead of DecodingException; mixing JJWT versions where a decoder returns unexpected types; byte[] vs String misuse with a decoder expecting a different input type.
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.
- Parsing and encoding errors: unexpected token, malformed input — why parsers reject input and how to find the real culprit.
Related errors
- Illegal ${name} character: '${c}'
- Unable to ${codecName}-decode ${name}: ${t.getMessage()}
- Unexpected unsecured Claims JWT.
- Unexpected content JWS.
- Unexpected Claims JWS.
AI-assisted analysis of jwtk/jjwt@fb71496164 (2026-09-09).
Data as JSON: /api/errors/25db8859e896d63a.
Report an issue: GitHub.