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

Cannot serialize ${name} to JSON. Cause: ${t.getMessage()}

Error message

Cannot serialize ${name} to JSON. Cause: ${t.getMessage()}

What it means

Thrown as a SerializationException when the delegate JSON serializer fails to write the given claims Map to the output stream. The message names the entity being serialized (e.g. 'JWT payload') and includes the underlying cause. Any Throwable during delegate.serialize is wrapped here.

Source

Thrown at impl/src/main/java/io/jsonwebtoken/impl/io/NamedSerializer.java:42

import java.util.Map;

public class NamedSerializer extends AbstractSerializer<Map<String, ?>> {

    private final String name;
    private final Serializer<Map<String, ?>> DELEGATE;

    public NamedSerializer(String name, Serializer<Map<String, ?>> serializer) {
        this.DELEGATE = Assert.notNull(serializer, "JSON Serializer cannot be null.");
        this.name = Assert.hasText(name, "Name cannot be null or empty.");
    }

    @Override
    protected void doSerialize(Map<String, ?> m, OutputStream out) throws SerializationException {
        try {
            this.DELEGATE.serialize(m, out);
        } catch (Throwable t) {
            String msg = String.format("Cannot serialize %s to JSON. Cause: %s", this.name, t.getMessage());
            throw new SerializationException(msg, t);
        }
    }
}

View on GitHub (pinned to fb71496164)

Solutions

  1. Inspect the cause message: unserializable types are the most frequent root cause.
  2. Convert claim values to JSON-friendly types (String, Number, Boolean, Map, List) before serializing.
  3. Register a JJWT Serializer (e.g. Jackson or Gson based) that supports your custom types via converters.
  4. Verify the target OutputStream is open and writable.

Example fix

// before
claims.put("expires", new Date()); // unserializable without converter
Jwts.builder().setClaims(claims).compact();
// after
claims.put("expires", new Date().getTime() / 1000); // JSON-friendly number
Jwts.builder().setClaims(claims).compact();
Defensive patterns

Strategy: type-guard

Validate before calling

// Java
static void assertJsonFriendly(Map<String,?> claims) {
    for (Map.Entry<String,?> e : claims.entrySet()) {
        Object v = e.getValue();
        boolean ok = v == null || v instanceof String || v instanceof Number
            || v instanceof Boolean || v instanceof Map || v instanceof List;
        if (!ok) throw new IllegalArgumentException(
            "Claim '" + e.getKey() + "' has non-JSON type: " + v.getClass().getName());
    }
}

Type guard

static boolean isJsonFriendly(Object v) {
    return v == null || v instanceof String || v instanceof Number
        || v instanceof Boolean || v instanceof Map || v instanceof List;
}

Try / catch

try {
    String jwt = Jwts.builder().setClaims(claims).compact();
} catch (SerializationException e) {
    log.error("Claims not serializable: {}", e.getMessage(), e.getCause());
    throw new IllegalArgumentException("Claims contain non-JSON-friendly values", e);
}

Prevention

When it happens

Trigger: Calling serialize on NamedSerializer with a Map containing values the JSON serializer cannot handle (unserializable types, cyclic references, non-JSON-friendly objects like Date without a converter) or when the target OutputStream fails.

Common situations: Putting custom Java objects (Date, BigDecimal with unusual settings, POJOs without registered converters) into claims; cyclic object graphs; OutputStream write failures (closed stream, disk full).

Understand the failure class

Background: json.Marshal / "failed to marshal" errors in Go: why "unsupported type" happens and how to fix it — this error's family across 22 libraries.

Related errors


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