quarkusio/quarkus · error · IllegalStateException

Either @ConfigRoot or @ConfigMapping is missing on ${configR

Error message

Either @ConfigRoot or @ConfigMapping is missing on ${configRoot}

What it means

This IllegalStateException is thrown by the Quarkus annotation processor while scanning @ConfigRoot-annotated classes during config documentation generation. Every config root class must carry both the Quarkus @ConfigRoot (or legacy @ConfigRoot) annotation and MicroProfile @ConfigMapping; if either annotation mirror cannot be resolved on the element, processing aborts. It indicates a malformed or inconsistent configuration class definition in an extension.

Source

Thrown at core/processor/src/main/java/io/quarkus/annotation/processor/documentation/config/scanner/ConfigMappingListener.java:74

                configRootAnnotation = annotationMirror;
                continue;
            }
            if (annotationName.equals(Types.ANNOTATION_CONFIG_MAPPING)) {
                configMappingAnnotion = annotationMirror;
                continue;
            }
            if (annotationName.equals(Types.ANNOTATION_CONFIG_DOC_PREFIX)) {
                configDocPrefixAnnotation = annotationMirror;
                continue;
            }
            if (annotationName.equals(Types.ANNOTATION_CONFIG_DOC_FILE_NAME)) {
                configDocFileNameAnnotation = annotationMirror;
                continue;
            }
        }

        if (configRootAnnotation == null || configMappingAnnotion == null) {
            throw new IllegalStateException("Either @ConfigRoot or @ConfigMapping is missing on " + configRoot);
        }

        final Map<? extends ExecutableElement, ? extends AnnotationValue> elementValues = configRootAnnotation
                .getElementValues();
        for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : elementValues.entrySet()) {
            if ("phase()".equals(entry.getKey().toString())) {
                configPhase = ConfigPhase.valueOf(entry.getValue().getValue().toString());
            }
        }

        validateRuntimeConfigOnDeploymentModules(configPhase, configRoot);

        for (Map.Entry<? extends ExecutableElement, ? extends AnnotationValue> entry : configMappingAnnotion.getElementValues()
                .entrySet()) {
            if ("prefix()".equals(entry.getKey().toString())) {
                prefix = entry.getValue().getValue().toString();
            }
        }

View on GitHub (pinned to e1c734241f)

Solutions

  1. Add the missing annotation: annotate the class with both @ConfigMapping and @ConfigRoot (with phase, e.g. @ConfigRoot(phase = ConfigPhase.BUILD_TIME)).
  2. If the class is legacy-style, either add @ConfigMapping or convert it fully to the @ConfigMapping model per current Quarkus conventions.
  3. Verify the annotations are on the same TypeElement the processor scans (not a superclass) and are runtime-retained source-visible as required.
  4. Rebuild the extension module with ./mvnw install to re-run annotation processing and confirm the fix.

Example fix

// before
@ConfigMapping(prefix = "quarkus.my-ext")
public interface MyExtConfig { }
// after
@ConfigRoot(phase = ConfigPhase.BUILD_TIME)
@ConfigMapping(prefix = "quarkus.my-ext")
public interface MyExtConfig { }
Defensive patterns

Strategy: validation

Validate before calling

if (!typeElement.getAnnotationMirrors().stream()
        .map(a -> a.getAnnotationType().toString())
        .anyMatch(n -> n.endsWith("ConfigRoot"))) {
    throw new IllegalStateException(typeElement + " must be annotated with @ConfigRoot");
}

Type guard

boolean hasAnnotation(TypeElement e, String simpleName) {
    return e.getAnnotationMirrors().stream()
        .anyMatch(a -> a.getAnnotationType().asElement().getSimpleName()
            .contentEquals(simpleName));
}

Try / catch

try {
    compile(extensionModule);
} catch (IllegalStateException e) {
    if (e.getMessage() != null && e.getMessage().contains("@ConfigRoot or @ConfigMapping is missing")) {
        fixConfigAnnotations(e);
    } else throw e;
}

Prevention

When it happens

Trigger: Compiling an extension whose config class is annotated with @ConfigMapping but lacks @ConfigRoot, or vice versa, or when the annotation is present via inheritance/indirection such that the annotation processor cannot find its mirror on the TypeElement passed to onConfigRoot.

Common situations: Adding a new @ConfigMapping interface to an extension and forgetting the Quarkus @ConfigRoot annotation; upgrading Quarkus and migrating off legacy config classes; copy-pasting a config class and dropping one annotation.

Related errors


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