gradle/gradle · error · UnsupportedOperationException

Cannot convert object of %s to %s.

Error message

Cannot convert object of %s to %s.

What it means

The Tooling API's ProtocolToModelAdapter adapts provider-side protocol objects into the consumer's model interfaces. Its convert() dispatch knows how to build maps, collections, primitives and interface-backed proxy views; when the requested target java.lang.reflect.Type matches none of those strategies, it throws this UnsupportedOperationException, naming both the concrete source class and the unsupported target type.

Source

Thrown at platforms/ide/tooling-api/src/main/java/org/gradle/tooling/internal/adapter/ProtocolToModelAdapter.java:369

                if (Iterable.class.isAssignableFrom(rawClass)) {
                    Type targetElementType = getElementType(parameterizedTargetType, 0);
                    return convertCollectionInternal(rawClass, targetElementType, (Iterable<?>) sourceObject, decoration, graphDetails);
                }
                if (Map.class.isAssignableFrom(rawClass)) {
                    Type targetKeyType = getElementType(parameterizedTargetType, 0);
                    Type targetValueType = getElementType(parameterizedTargetType, 1);
                    return convertMap(rawClass, targetKeyType, targetValueType, (Map<?, ?>) sourceObject, decoration, graphDetails);
                }
            }
        }
        if (targetType instanceof Class) {
            Class<Object> targetClassType = Cast.uncheckedNonnullCast(targetType);
            if (targetClassType.isPrimitive()) {
                return sourceObject;
            }
            return createView(targetClassType, sourceObject, decoration, graphDetails);
        }
        throw new UnsupportedOperationException(String.format("Cannot convert object of %s to %s.", sourceObject.getClass(), targetType));
    }

    private static Map<Object, Object> convertMap(Class<?> mapClass, Type targetKeyType, Type targetValueType, Map<?, ?> sourceObject, ViewDecoration decoration, ViewGraphDetails graphDetails) {
        Map<Object, Object> convertedElements = COLLECTION_MAPPER.createEmptyMap(mapClass);
        for (Map.Entry<?, ?> entry : sourceObject.entrySet()) {
            convertedElements.put(convert(targetKeyType, entry.getKey(), decoration, graphDetails), convert(targetValueType, entry.getValue(), decoration, graphDetails));
        }
        return convertedElements;
    }

    private static Object convertCollectionInternal(Class<?> collectionClass, Type targetElementType, Iterable<?> sourceObject, ViewDecoration decoration, ViewGraphDetails graphDetails) {
        Collection<Object> convertedElements = COLLECTION_MAPPER.createEmptyCollection(collectionClass);
        convertCollectionInternal(convertedElements, targetElementType, sourceObject, decoration, graphDetails);
        if (collectionClass.equals(DomainObjectSet.class)) {
            return new ImmutableDomainObjectSet<Object>(convertedElements);
        } else {
            return convertedElements;
        }

View on GitHub (pinned to 534f27719b)

Solutions

  1. Simplify the tooling model interface to types the adapter supports: primitives, String, File, enums, List/Set/Map of supported types, and nested model interfaces.
  2. Align the consumer's Tooling API artifact version with the target Gradle version so both sides agree on the model shapes.
  3. If you drive ProtocolToModelAdapter yourself, pass a concrete Class or a supported parameterized type (List<T>, Map<K,V>) instead of an unresolved generic Type.

Example fix

// before
public interface CustomModel {
    <T> Type metadata(); // adapter cannot convert to an unresolved generic Type
}

// after
public interface CustomModel {
    CustomMetadata metadata(); // a nested model interface the adapter can proxy
}
Defensive patterns

Strategy: try-catch

Validate before calling

static boolean usesSupportedModelTypes(Class<?> model) {
    for (Method m : model.getMethods()) {
        Type t = m.getGenericReturnType();
        if (t instanceof TypeVariable || t instanceof WildcardType || t instanceof GenericArrayType) return false;
        if (t instanceof ParameterizedType) {
            for (Type arg : ((ParameterizedType) t).getActualTypeArguments()) {
                if (!(arg instanceof Class)) return false;
            }
        }
    }
    return true;
}
// before connecting: if (!usesSupportedModelTypes(MyModel.class)) fail with your own message

Try / catch

try {
    MyModel model = connection.model(MyModel.class).get();
} catch (UnsupportedOperationException e) {
    // message names source class and unsupported target type; log both, then
    // fix the model interface or align consumer/provider versions
}

Prevention

When it happens

Trigger: Fetching a tooling model (ProjectConnection.model(...) or a BuildAction result) whose interface declares a method whose return or element type the adapter cannot map: an unresolved type variable, a wildcard or generic-array type, or another exotic Type that is neither a plain Class nor a handled parameterized type. Also reachable when code reuses ProtocolToModelAdapter.convert/unpack directly with a raw Type.

Common situations: Custom tooling-model plugins exposing generic-heavy or unusual types; version skew where a newer Tooling API consumer talks to an older Gradle provider (or vice versa) so the conversion tables disagree; model interfaces that evolved between Gradle versions.

Related errors


AI-assisted analysis of gradle/gradle@534f27719b (2026-08-22). Data as JSON: /api/errors/a6030d892b876a4d. Report an issue: GitHub.