quarkusio/quarkus · error · DeploymentException

Cannot directly return collections or arrays using JAXB. You

Error message

Cannot directly return collections or arrays using JAXB. You need to wrap it into a root element class. Problematic method is '{entry.getActualClassInfo().name}.{methodInfo.name}'

What it means

When producing XML (application/xml) via JAXB, a JAX-RS resource method cannot directly return a Collection or array — JAXB requires a root element class to marshal. During deployment, RESTEasy Reactive's JAXB processor scans resource methods whose @Produces includes an XML media type, and if the effective return type is not JAXB-compatible (collection/array without a JAXB root), the build fails with this DeploymentException pointing at the offending method.

Source

Thrown at extensions/resteasy-reactive/rest-jaxb/deployment/src/main/java/io/quarkus/resteasy/reactive/jaxb/deployment/ResteasyReactiveJaxbProcessor.java:97

    }

    @BuildStep
    void registerClassesToBeBound(ResteasyReactiveResourceMethodEntriesBuildItem resourceMethodEntries,
            JaxRsResourceIndexBuildItem index,
            BuildProducer<JaxbClassesToBeBoundBuildItem> classesToBeBoundBuildItemBuildProducer) {
        Set<ClassInfo> classesInfo = new HashSet<>();

        IndexView indexView = index.getIndexView();
        for (ResteasyReactiveResourceMethodEntriesBuildItem.Entry entry : resourceMethodEntries.getEntries()) {
            ResourceMethod resourceInfo = entry.getResourceMethod();
            MethodInfo methodInfo = entry.getMethodInfo();
            ClassInfo effectiveReturnType = getEffectiveClassInfo(methodInfo.returnType(), indexView);

            if (effectiveReturnType != null) {
                // When using "application/xml", the return type needs to be registered
                if (producesXml(resourceInfo)) {
                    if (!isTypeCompatibleWithJaxb(methodInfo.returnType())) {
                        throw new DeploymentException(
                                "Cannot directly return collections or arrays using JAXB. You need to wrap it "
                                        + "into a root element class. Problematic method is '"
                                        + entry.getActualClassInfo().name() + "." + methodInfo.name() + "'");
                    }

                    classesInfo.add(effectiveReturnType);
                }

                // When using "multipart/form-data", the parts that use "application/xml" need to be registered
                if (producesMultipart(resourceInfo)) {
                    classesInfo.addAll(getEffectivePartsUsingXml(effectiveReturnType, indexView));
                }
            }

            // If consumes "application/xml" or "multipart/form-data", we register all the classes of the parameters
            boolean consumesXml = consumesXml(resourceInfo);
            boolean consumesMultipart = consumesMultipart(resourceInfo);
            if (consumesXml || consumesMultipart) {

View on GitHub (pinned to e1c734241f)

Solutions

  1. Wrap the collection in a JAXB root element class (e.g. a @XmlRootElement wrapper holding a List<T> with @XmlElement wrapping)
  2. Change the endpoint to @Produces(MediaType.APPLICATION_JSON) if XML output is not actually needed
  3. Narrow the method's @Produces so it does not advertise XML if the return type cannot be marshalled
  4. Return a single @XmlRootElement entity instead of a collection, or stream via a custom MessageBodyWriter

Example fix

// before
@GET
@Produces(MediaType.APPLICATION_XML)
public List<User> users() { ... }

// after
@XmlRootElement(name = "users")
class Users {
    @XmlElement(name = "user")
    public List<User> users;
}

@GET
@Produces(MediaType.APPLICATION_XML)
public Users users() { ... }
Defensive patterns

Strategy: type-guard

Validate before calling

static boolean isXmlCompatible(Class<?> rt) {
    if (Collection.class.isAssignableFrom(rt) || rt.isArray()) return false;
    return rt.isAnnotationPresent(jakarta.xml.bind.annotation.XmlRootElement.class);
}
// before declaring @Produces(APPLICATION_XML) on a method, assert:
if (!isXmlCompatible(method.getReturnType())) throw new IllegalStateException("Wrap the return type in a JAXB root element");

Type guard

static boolean hasJaxbRoot(Class<?> c) {
    return c.isAnnotationPresent(jakarta.xml.bind.annotation.XmlRootElement.class);
}

Try / catch

try {
    deploy();
} catch (DeploymentException e) {
    if (e.getMessage() != null && e.getMessage().contains("Cannot directly return collections or arrays using JAXB")) {
        // wrap the return type named in the message
    }
}

Prevention

When it happens

Trigger: A resource method annotated (explicitly or by negotiation) with @Produces(MediaType.APPLICATION_XML) (or application/*+xml) returns List<T>, Set<T>, T[], Response wrapping such, or any type failing the JAXB root-element compatibility check.

Common situations: Returning List<MyEntity> from an endpoint that defaults to XML because the client sends Accept: application/xml; switching @Produces from JSON to XML without restructuring the return type; copying JSON endpoints to XML endpoints.

Related errors


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