oracle/graal · error · IllegalArgumentException

methods with same signature {} but incompatible return types

Error message

methods with same signature {} but incompatible return types: {} and others

What it means

Thrown by EspressoForeignProxyGenerator's return-type coverage check: when two proxied interfaces declare the same method signature but their return types are incompatible, the first failure mode triggers if the newly seen return type is primitive — a primitive can never be 'covered' by a reference return type, so the proxy cannot implement both methods and creation aborts. Message includes the friendly signature and the offending primitive type.

Source

Thrown at espresso/src/com.oracle.truffle.espresso/src/com/oracle/truffle/espresso/nodes/interop/EspressoForeignProxyGenerator.java:731

    private static void checkReturnTypes(List<ProxyMethod> methods) {
        /*
         * If there is only one method with a given signature, there cannot be a conflict. This is
         * the only case in which a primitive (or void) return type is allowed.
         */
        if (methods.size() < 2) {
            return;
        }

        /*
         * List of return types that are not yet known to be assignable from ("covered" by) any of
         * the others.
         */
        LinkedList<Klass> uncoveredReturnTypes = new LinkedList<>();

        nextNewReturnType: for (ProxyMethod pm : methods) {
            Klass newReturnType = pm.returnType.getRawType();
            if (newReturnType.isPrimitive()) {
                throw new IllegalArgumentException(
                                "methods with same signature " +
                                                getFriendlyMethodSignature(pm.methodName,
                                                                pm.parameterTypes) +
                                                " but incompatible return types: " +
                                                newReturnType.getName() + " and others");
            }
            boolean added = false;

            /*
             * Compare the new return type to the existing uncovered return types.
             */
            ListIterator<Klass> liter = uncoveredReturnTypes.listIterator();
            while (liter.hasNext()) {
                Klass uncoveredReturnType = liter.next();

                /*
                 * If an existing uncovered return type is assignable to this new one, then we can
                 * forget the new one.

View on GitHub (pinned to a66e9ccd1d)

Solutions

  1. Do not proxy those two interfaces together — pick one, or wrap one behind an adapter object.
  2. Change one interface's method return type so they are assignable (e.g. both reference types with a common subtype).
  3. If you control the interfaces, rename the colliding method on one of them.

Example fix

// before
interface A { int fetch(); }
interface B { Object fetch(); }
proxy(A.class, B.class); // throws

// after
interface B { Object fetchValue(); } // renamed, no clash
proxy(A.class, B.class);
Defensive patterns

Strategy: validation

Validate before calling

// reject proxies over same-signature methods with primitive vs reference returns
Map<String, Klass> returnsBySignature = new HashMap<>();
for (Method m : allMethods(interfaces)) {
    String sig = m.getName() + Arrays.toString(m.getParameterTypes());
    Klass prev = returnsBySignature.putIfAbsent(sig, m.getReturnType());
    if (prev != null && !prev.isAssignableTo(m.getReturnType()) && !m.getReturnType().isAssignableTo(prev)) {
        throw new IllegalArgumentException("incompatible returns for " + sig);
    }
}

Try / catch

try {
    generator.getProxy(context, loader, interfaces);
} catch (IllegalArgumentException e) {
    // message contains the conflicting signature: split the proxy or fix the interfaces
}

Prevention

When it happens

Trigger: Proxying interfaces where one declares 'int m()' and another declares 'Object m()' (same name and parameter types): the primitive return type hits the isPrimitive() branch and throws. Replicates java.lang.reflect.Proxy's 'methods with same signature but incompatible return types' error.

Common situations: Cobbling together proxies over unrelated third-party interfaces that happen to share a method name; version drift where one interface's method signature changed its return type; bridge-style APIs mixing boxed and primitive returns.

Related errors


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