jwtk/jjwt · error · io.jsonwebtoken.io.DeserializationException

Deserialized data resulted in a null value; cannot create Ma

Error message

Deserialized data resulted in a null value; cannot create Map<String,?>

What it means

Thrown as a DeserializationException when the JSON deserializer produces null from the input, so a Map<String,?> cannot be constructed. JsonObjectDeserializer expects deserializable JSON object content; a null result means the input was empty, literal 'null', or otherwise deserialized to nothing.

Source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/io/JsonObjectDeserializer.java:55

            "investigate the source further. Cause: %s";

    private final Deserializer<?> deserializer;
    private final String name;

    public JsonObjectDeserializer(Deserializer<?> deserializer, String name) {
        this.deserializer = Assert.notNull(deserializer, "JSON Deserializer cannot be null.");
        this.name = Assert.hasText(name, "name cannot be null or empty.");
    }

    @Override
    public Map<String, ?> apply(Reader in) {
        Assert.notNull(in, "InputStream argument cannot be null.");
        Object value;
        try {
            value = this.deserializer.deserialize(in);
            if (value == null) {
                String msg = "Deserialized data resulted in a null value; cannot create Map<String,?>";
                throw new DeserializationException(msg);
            }
            if (!(value instanceof Map)) {
                String msg = "Deserialized data is not a JSON Object; cannot create Map<String,?>";
                throw new DeserializationException(msg);
            }
            // JSON Specification requires all JSON Objects to have string-only keys.  So instead of
            // checking that the val.keySet() has all Strings, we blindly cast to a Map<String,?>
            // since input would rarely, if ever, have non-string keys.
            //noinspection unchecked
            return (Map<String, ?>) value;
        } catch (StackOverflowError e) {
            String msg = String.format(MALFORMED_COMPLEX_ERROR, this.name, this.name, e.getMessage());
            throw new DeserializationException(msg, e);
        } catch (Throwable t) {
            throw malformed(t);
        }
    }

View on GitHub (pinned to fb71496164)

Solutions

  1. Verify the input stream actually contains JSON object bytes before deserializing.
  2. Check the stream position — ensure it was not already read/consumed elsewhere.
  3. Ensure the producer serialized actual JSON object content, not null.
  4. Add a caller-side check: if the decoded byte array is empty, fail before calling the deserializer.

Example fix

// before
Map<String,?> claims = deserializer.deserialize(new ByteArrayInputStream(payload));
// after
if (payload == null || payload.length == 0) {
    throw new IllegalArgumentException("payload is empty");
}
Map<String,?> claims = deserializer.deserialize(new ByteArrayInputStream(payload));
Defensive patterns

Strategy: validation

Validate before calling

// Java
if (payload == null || payload.length == 0) {
    throw new IllegalArgumentException("payload must be non-empty JSON object bytes");
}
String s = new String(payload, StandardCharsets.UTF_8).trim();
if (s.isEmpty() || s.equals("null")) throw new IllegalArgumentException("payload deserializes to null");

Type guard

static boolean isNonEmptyJson(byte[] bytes) {
    if (bytes == null || bytes.length == 0) return false;
    String s = new String(bytes, StandardCharsets.UTF_8).trim();
    return !s.isEmpty() && !s.equalsIgnoreCase("null");
}

Try / catch

try {
    Map<String,?> claims = deserializer.apply(in);
} catch (DeserializationException e) {
    throw new IllegalArgumentException("Claims payload is empty or null: " + e.getMessage());
}

Prevention

When it happens

Trigger: Calling deserialize with an empty InputStream, an InputStream containing only whitespace or the JSON literal 'null', or a deserializer configured to return null for unrecognized input.

Common situations: Passing an empty JWT payload segment (decoded to zero bytes); reading from a stream that was already consumed; serializing a Java null claims map earlier and then trying to deserialize it back.

Understand the failure class

Background: "failed to unmarshal" / json.Unmarshal errors: why parsing a response into a Go struct fails and how to fix it — this error's family across 23 libraries.

Related errors


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