quarkusio/quarkus · error · RuntimeException

You cannot invoke ${method.getName()}() directly on an objec

Error message

You cannot invoke ${method.getName()}() directly on an object returned from the bytecode recorder, you can only pass it back into the recorder as a parameter

What it means

Objects returned from @Record method invocations are not real instances — they are proxies that record the invoked method calls into bytecode for later replay. Calling methods like hashCode/toString (other than the specially handled equals) directly on such a proxy cannot be recorded, so Quarkus throws. The proxy may only be passed back into the recorder as an argument of another recorded call.

Source

Thrown at core/deployment/src/main/java/io/quarkus/deployment/recording/BytecodeRecorderImpl.java:1726

                return staticInit;
            }
            if (method.getName().equals("toString")
                    && method.getParameterCount() == 0
                    && method.getReturnType().equals(String.class)) {
                return "Runtime proxy of " + returnType + " with id " + key;
            }
            if (method.getName().equals("hashCode")
                    && method.getParameterCount() == 0
                    && method.getReturnType().equals(int.class)) {
                return System.identityHashCode(proxy);
            }
            if (method.getName().equals("equals")
                    && method.getParameterCount() == 1
                    && method.getParameterTypes()[0] == Object.class
                    && method.getReturnType().equals(boolean.class)) {
                return proxy == args[0];
            }
            throw new RuntimeException(
                    "You cannot invoke " + method.getName()
                            + "() directly on an object returned from the bytecode recorder, you can only pass it back into the recorder as a parameter");
        }
    }

    interface BytecodeInstruction {

    }

    public interface ReturnedProxy {
        String __returned$proxy$key();

        boolean __static$$init();
    }

    static final class StoredMethodCall implements BytecodeInstruction {
        final Class<?> theClass;
        final Method method;

View on GitHub (pinned to e1c734241f)

Solutions

  1. Do not call any methods on the returned proxy except passing it into another recorded recorder call
  2. Exclude the object from logging/debugging code (or use its class name, not the object)
  3. For tests, assert on recorded behavior at runtime rather than on the proxy object

Example fix

// before
System.out.println("got: " + recorder.getThing()); // calls toString
// after
// don't touch the proxy; only pass it on:
otherRecorder.use(recorder.getThing());
Defensive patterns

Strategy: try-catch

Type guard

static boolean isRecorderProxy(Object o) {
    return o != null && Proxy.isProxyClass(o.getClass());
}
// only pass proxies back into recorder calls; never log/store them

Try / catch

try {
    sink.use(recorder.getThing()); // OK: passed back into recorder
} catch (RuntimeException e) {
    if (String.valueOf(e.getMessage()).contains("You cannot invoke")) {
        throw new IllegalStateException("Do not call methods on recorder-returned proxies", e);
    }
    throw e;
}

Prevention

When it happens

Trigger: Logging or debugging a value returned from a recorder-invoked method (accidentally invoking toString()/hashCode()); using the returned object in a Map/Set (which calls hashCode/equals); invoking a business method on it at build time expecting a real result.

Common situations: System.out.println of recorder return values during extension development; storing proxy objects in collections used at build time; assertion failures printing proxies in extension tests.

Related errors


AI-assisted analysis of quarkusio/quarkus@e1c734241f (2026-09-05). Data as JSON: /api/errors/518d88376a8522c2. Report an issue: GitHub.