oracle/graal · error · IllegalArgumentException

Failed to serialize ${op} due to: ${cause}

Error message

Failed to serialize ${op} due to: ${cause}

What it means

JsonReplayCodec's RecordedOperation serializer wraps a lower-level IllegalArgumentException that escaped while serializing the receiver, method name list, arguments, or result marker of a recorded operation. The message chains the original cause, so the real failure is in e.getMessage() and the cause chain; the operation itself (op.toString()) is included to identify which recorded call failed.

Source

Thrown at compiler/src/jdk.graal.compiler/src/jdk/graal/compiler/hotspot/replaycomp/JsonReplayCodec.java:202

            return TAG;
        }

        @Override
        public void serialize(Object instance, JsonBuilder.ObjectBuilder objectBuilder, RecursiveSerializer serializer) throws IOException {
            OperationRecorder.RecordedOperation op = (OperationRecorder.RecordedOperation) instance;
            try {
                serializer.serialize(op.receiver(), objectBuilder.append("recv"));
                objectBuilder.append("method", Arrays.asList(op.method().methodAndParamNames()));
                if (op.args() != null) {
                    try (JsonBuilder.ArrayBuilder arrayBuilder = objectBuilder.append("args").array()) {
                        for (Object arg : op.args()) {
                            serializer.serialize(arg, arrayBuilder.nextEntry());
                        }
                    }
                }
                serializer.serialize(op.resultOrMarker(), objectBuilder.append("res"));
            } catch (IllegalArgumentException e) {
                throw new IllegalArgumentException("Failed to serialize " + op + " due to: " + e.getMessage(), e);
            }
        }

        @SuppressWarnings("unchecked")
        @Override
        public OperationRecorder.RecordedOperation deserialize(EconomicMap<String, Object> json, RecursiveDeserializer deserializer, CompilationProxies.ProxyFactory proxyFactory)
                        throws DeserializationException {
            Object recv = deserializer.deserialize(json.get("recv"), proxyFactory);
            List<Object> methodProp = (List<Object>) json.get("method");
            String[] methodArray = new String[methodProp.size()];
            for (int i = 0; i < methodArray.length; i++) {
                methodArray[i] = (String) methodProp.get(i);
            }
            CompilationProxy.SymbolicMethod method = new CompilationProxy.SymbolicMethod(methodArray);
            List<Object> list = (List<Object>) json.get("args");
            Object[] args;
            if (list == null) {
                args = null;

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Read the chained cause ('due to: ...') — it names the actual unserializable value; register an ObjectSerializer for that class in JsonReplayCodec
  2. Ensure recording and JSON serialization run on the same GraalVM/compiler build
  3. If the type is incidental, avoid recording it (narrow the recorded compiler-interface surface)
  4. Add a serializer analogous to the existing ones (clazz()/tag()/serialize/deserialize) and register it in the codec's serializer table

Example fix

// before: no serializer for new type X -> 'Failed to serialize op ... due to: No serializer for class X'
// after: register one
// tagSerializers.put("x", new ObjectSerializer() {
//     public Class<?> clazz() { return X.class; }
//     public String tag() { return "x"; }
//     public void serialize(Object i, JsonBuilder.ObjectBuilder b, RecursiveSerializer s) throws IOException { ... }
//     public Object deserialize(EconomicMap<String,Object> j, RecursiveDeserializer d, CompilationProxies.ProxyFactory p) { ... }
// });
Defensive patterns

Strategy: try-catch

Try / catch

try {
    codec.write(compilationUnit, writer);
} catch (IllegalArgumentException e) {
    Throwable root = getRootCause(e); // walk e.getCause() chain
    // root names the actual unserializable value; report it, do not retry blindly
    throw new ReplaySerializationException("op failed: " + e.getMessage(), root);
}

Prevention

When it happens

Trigger: Serializing a RecordedOperation whose receiver or argument object has no registered ObjectSerializer (propagates as IllegalArgumentException from RecursiveSerializer), or whose method()/args()/resultOrMarker() returns something the codec cannot encode.

Common situations: Recording a compilation that touches a compiler-interface type for which no serializer was registered (new JDK/Graal version adds a type); a recorded value being of an exotic class; serializing proxies created by a mismatched codec version.

Related errors


AI-assisted analysis of oracle/graal@a66e9ccd1d (2026-08-14). Data as JSON: /api/errors/f7a529280d1c6c8a. Report an issue: GitHub.