jwtk/jjwt · error · SerializationException

Unable to serialize object of type ${className}: ${e.getMess

Error message

Unable to serialize object of type ${className}: ${e.getMessage()}

What it means

JJWT wraps any unexpected Throwable thrown while serializing an object (typically the JWT claim map) into bytes into a SerializationException. AbstractSerializer.serialize catches failures from doSerialize that are not already SerializationException instances and rethrows them with the object's class name and the underlying message. The class name in the message tells you exactly which object failed to convert.

Source

Thrown at api/src/main/java/io/jsonwebtoken/io/AbstractSerializer.java:60

    public final byte[] serialize(T t) throws SerializationException {
        ByteArrayOutputStream out = new ByteArrayOutputStream();
        serialize(t, out);
        return out.toByteArray();
    }

    /**
     * {@inheritDoc}
     */
    @Override
    public final void serialize(T t, OutputStream out) throws SerializationException {
        try {
            doSerialize(t, out);
        } catch (Throwable e) {
            if (e instanceof SerializationException) {
                throw (SerializationException) e;
            }
            String msg = "Unable to serialize object of type " + Objects.nullSafeClassName(t) + ": " + e.getMessage();
            throw new SerializationException(msg, e);
        }
    }

    /**
     * Converts the specified Java object into a formatted data byte stream, writing the bytes to the specified
     * {@code out}put stream.
     *
     * @param t   the object to convert to a byte stream
     * @param out the stream to write to
     * @throws Exception if there is a problem converting the object to a byte stream or writing the
     *                   bytes to the {@code out}put stream.
     * @since 0.12.0
     */
    protected abstract void doSerialize(T t, OutputStream out) throws Exception;
}

View on GitHub (pinned to fb71496164)

Solutions

  1. Read the cause and the class name in the message; ensure that type is JSON-serializable by the configured runtime (Jackson or Gson)
  2. Simplify claim values to primitives, Strings, Dates, Maps, and Lists, or register a serializer/converter for the custom type
  3. Fix Jackson visibility issues (add getters or @JsonProperty/@JsonAutoDetect) when 'No serializer found' is the cause
  4. If you implemented a custom Serializer, wrap failures in SerializationException inside doSerialize so they propagate unchanged

Example fix

// before
jws = Jwts.builder().setClaims(Map.of("user", hibernateLazyProxy)).compact();
// Unable to serialize object of type User$HibernateProxy$...: ...

// after
jws = Jwts.builder().claim("userId", user.getId()).compact(); // serialize plain values
Defensive patterns

Strategy: validation

Validate before calling

// Java: ensure claim values are JSON-safe before building the JWT
static boolean isJsonSafe(Object v) {
    if (v == null) return true;
    if (v instanceof String || v instanceof Number || v instanceof Boolean) return true;
    if (v instanceof java.util.Date || v instanceof java.util.Calendar) return true;
    if (v instanceof Map<?,?> m) return m.values().stream().allMatch(MyClaims::isJsonSafe);
    if (v instanceof java.util.Collection<?> c) return c.stream().allMatch(MyClaims::isJsonSafe);
    if (v instanceof byte[] || v instanceof char[]) return true;
    return false;
}

Type guard

boolean isSerializableClaim(Object v) {
    return v == null || v instanceof String || v instanceof Number
        || v instanceof Boolean || v instanceof java.util.Date
        || v instanceof Map || v instanceof java.util.Collection;
}

Try / catch

try {
    String jws = Jwts.builder().setClaims(claims).signWith(key).compact();
} catch (SerializationException e) {
    // e.getCause() holds the underlying serializer failure
}

Prevention

When it happens

Trigger: Serializing a claims map or payload containing a value the underlying JSON runtime cannot handle — e.g. an object with no Jackson/Gson serializer, a JDK type the runtime rejects, circular object references, or a custom Serializer whose doSerialize throws (IOException, IllegalArgumentException, etc.).

Common situations: Putting non-JSON-friendly values in claims (Date handled by default, but custom types like BufferedImage, Hibernate lazy proxies, or enums without registered serializers); Jackson failing with 'No serializer found' due to no getters and visibility config; classpath version mismatch between JJWT and Jackson/Gson; a custom Serializer that throws raw exceptions.

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/5cd45bb94b53ff57. Report an issue: GitHub.