{"record":{"id":"647a725a45ba57ce","repo":"apache/maven","slug":"only-string-keys-are-supported-for-maps","errorCode":null,"errorMessage":"Only String keys are supported for maps: {}","messagePattern":"Only String keys are supported for maps: (.+?)","errorType":"exception","errorClass":"DIException","httpStatus":null,"severity":"error","filePath":"impl/maven-core/src/main/java/org/apache/maven/internal/impl/SisuDiBridgeModule.java","lineNumber":244,"sourceCode":"                List<Binding<?>> list = new ArrayList<>();\n                // Add DI bindings\n                list.addAll(getBindings().getOrDefault(elementType, Set.of()));\n                // Add Plexus bindings\n                for (var bean : locator.get().locate(toGuiceKey(elementType))) {\n                    if (isPlexusBean(bean)) {\n                        list.add(new BindingToBeanEntry<>(elementType).toBeanEntry(bean));\n                    }\n                }\n                //noinspection unchecked\n                return (Q) list(list.stream().sorted(getPriorityComparator()).toList(), this::getInstance);\n            };\n        }\n\n        private <Q> Supplier<Q> getMapSupplier(Key<Q> key) {\n            Key<?> keyType = key.getTypeParameter(0);\n            Key<Object> valueType = key.getTypeParameter(1);\n            if (keyType.getRawType() != String.class) {\n                throw new DIException(\"Only String keys are supported for maps: \" + key);\n            }\n            return () -> {\n                var comparator = getPriorityComparator();\n                Map<String, Binding<?>> map = new HashMap<>();\n                for (Binding<?> b : getBindings().getOrDefault(valueType, Set.of())) {\n                    String name =\n                            b.getOriginalKey() != null && b.getOriginalKey().getQualifier() instanceof String s\n                                    ? s\n                                    : \"\";\n                    map.compute(name, (n, ob) -> ob == null || comparator.compare(ob, b) < 0 ? b : ob);\n                }\n                for (var bean : locator.get().locate(toGuiceKey(valueType))) {\n                    if (isPlexusBean(bean)) {\n                        Binding<?> b = new BindingToBeanEntry<>(valueType)\n                                .toBeanEntry(bean)\n                                .prioritize(bean.getRank());\n                        String name = bean.getKey() instanceof com.google.inject.name.Named n ? n.value() : \"\";\n                        map.compute(name, (n, ob) -> ob == null || ob.getPriority() < b.getPriority() ? b : ob);","sourceCodeStart":226,"sourceCodeEnd":262,"githubUrl":"https://github.com/apache/maven/blob/e4093d4e120eac99d6bdce5ba67cace2f3085c97/impl/maven-core/src/main/java/org/apache/maven/internal/impl/SisuDiBridgeModule.java#L226-L262","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Change the injection to Map<String, T> and convert the String key to your enum/Class where you consume it","Alternatively inject Set<T> (getAllBindings) and derive the key from each component yourself","If you truly need typed keys, bind a dedicated registry component that wraps the Map<String, T> internally","Keep @Named values stable — they become the String keys of the map"],"exampleFix":"// before\n@Inject Map<MyEnum, Handler> handlers; // DIException: only String keys\n\n// after\n@Inject Map<String, Handler> handlers;\n\nHandler h = handlers.get(myEnum.name());","handlingStrategy":"type-guard","validationCode":"// ensure the injection point signature uses a String key before deployment\nField f = getClass().getDeclaredField(\"handlers\");\nif (!Map.class.isAssignableFrom(f.getType()) || !(f.getGenericType() instanceof ParameterizedType pt)\n        || pt.getActualTypeArguments()[0] != String.class) {\n    throw new IllegalStateException(\"Map injection must be Map<String, ?>\");\n}","typeGuard":"boolean isSisuCompatibleMap(java.lang.reflect.Type t) {\n    if (!(t instanceof ParameterizedType pt) || !Map.class.isAssignableFrom((Class<?>) pt.getRawType())) return false;\n    return pt.getActualTypeArguments()[0] == String.class;\n}","tryCatchPattern":null,"preventionTips":["Standardize on Map<String, T> for injected component maps in Maven extensions","Convert enum/Class keys at the usage site, not at the injection point","Document that Sisu maps are keyed by the @Named value of each component"],"tags":["maven","dependency-injection","sisu","map-injection","generic-types"],"backgroundTag":"di-map-key-type-unsupported","analyzedSha":"e4093d4e120eac99d6bdce5ba67cace2f3085c97","analyzedAt":"2026-08-21T22:58:24.034Z","schemaVersion":2},"datasetVersion":"2026-08-21T23:17:16.201Z"}