apache/maven · error · DIException

Only String keys are supported for maps: {}

Error message

Only String keys are supported for maps: {}

What it means

Thrown by the Sisu bridge when a Map injection is requested whose key type parameter is not String. Legacy Plexus and the Sisu bridge expose component maps keyed by the component's String name (its @Named value or role-hint); arbitrary key types such as Class, enum, or custom objects have no mapping semantics, so the lookup fails fast with DIException.

Source

Thrown at impl/maven-core/src/main/java/org/apache/maven/internal/impl/SisuDiBridgeModule.java:244

                List<Binding<?>> list = new ArrayList<>();
                // Add DI bindings
                list.addAll(getBindings().getOrDefault(elementType, Set.of()));
                // Add Plexus bindings
                for (var bean : locator.get().locate(toGuiceKey(elementType))) {
                    if (isPlexusBean(bean)) {
                        list.add(new BindingToBeanEntry<>(elementType).toBeanEntry(bean));
                    }
                }
                //noinspection unchecked
                return (Q) list(list.stream().sorted(getPriorityComparator()).toList(), this::getInstance);
            };
        }

        private <Q> Supplier<Q> getMapSupplier(Key<Q> key) {
            Key<?> keyType = key.getTypeParameter(0);
            Key<Object> valueType = key.getTypeParameter(1);
            if (keyType.getRawType() != String.class) {
                throw new DIException("Only String keys are supported for maps: " + key);
            }
            return () -> {
                var comparator = getPriorityComparator();
                Map<String, Binding<?>> map = new HashMap<>();
                for (Binding<?> b : getBindings().getOrDefault(valueType, Set.of())) {
                    String name =
                            b.getOriginalKey() != null && b.getOriginalKey().getQualifier() instanceof String s
                                    ? s
                                    : "";
                    map.compute(name, (n, ob) -> ob == null || comparator.compare(ob, b) < 0 ? b : ob);
                }
                for (var bean : locator.get().locate(toGuiceKey(valueType))) {
                    if (isPlexusBean(bean)) {
                        Binding<?> b = new BindingToBeanEntry<>(valueType)
                                .toBeanEntry(bean)
                                .prioritize(bean.getRank());
                        String name = bean.getKey() instanceof com.google.inject.name.Named n ? n.value() : "";
                        map.compute(name, (n, ob) -> ob == null || ob.getPriority() < b.getPriority() ? b : ob);

View on GitHub (pinned to e4093d4e12)

Solutions

  1. Change the injection to Map<String, T> and convert the String key to your enum/Class where you consume it
  2. Alternatively inject Set<T> (getAllBindings) and derive the key from each component yourself
  3. If you truly need typed keys, bind a dedicated registry component that wraps the Map<String, T> internally
  4. Keep @Named values stable — they become the String keys of the map

Example fix

// before
@Inject Map<MyEnum, Handler> handlers; // DIException: only String keys

// after
@Inject Map<String, Handler> handlers;

Handler h = handlers.get(myEnum.name());
Defensive patterns

Strategy: type-guard

Validate before calling

// ensure the injection point signature uses a String key before deployment
Field f = getClass().getDeclaredField("handlers");
if (!Map.class.isAssignableFrom(f.getType()) || !(f.getGenericType() instanceof ParameterizedType pt)
        || pt.getActualTypeArguments()[0] != String.class) {
    throw new IllegalStateException("Map injection must be Map<String, ?>");
}

Type guard

boolean isSisuCompatibleMap(java.lang.reflect.Type t) {
    if (!(t instanceof ParameterizedType pt) || !Map.class.isAssignableFrom((Class<?>) pt.getRawType())) return false;
    return pt.getActualTypeArguments()[0] == String.class;
}

Prevention

When it happens

Trigger: Declaring an injected field or a lookup like Map<Class<?>, Handler> handlers or Map<MyEnum, Provider<X>>: getMapSupplier() inspects key.getTypeParameter(0) and throws unless it is exactly String.class. Also triggered by refactoring a Map<String, T> field to a different key type while porting Plexus components to JSR-330.

Common situations: Migrating old Plexus @Requirement map injections to javax/jakarta.inject and choosing a 'nicer' key type; writing a new Maven extension that wants a registry keyed by enum; copy-pasting a Guice MapBinder pattern (which supports any key type) into a Sisu environment.

Related errors


AI-assisted analysis of apache/maven@e4093d4e12 (2026-08-21). Data as JSON: /api/errors/647a725a45ba57ce. Report an issue: GitHub.