quarkusio/quarkus · error · DeploymentException

Endpoints that produce a Multipart result can only be used o

Error message

Endpoints that produce a Multipart result can only be used on blocking methods. Offending method is '%s#%s'

What it means

An endpoint that returns a Multipart result (server-sent multipart response) must be a blocking method, because streaming multipart output relies on blocking I/O. EndpointIndexer enforces that methods with returnsMultipart true also have blocking=true, otherwise deployment fails.

Source

Thrown at independent-projects/resteasy-reactive/common/processor/src/main/java/org/jboss/resteasy/reactive/common/processor/EndpointIndexer.java:783

            boolean blocking = isBlocking(currentMethodInfo, defaultBlocking);
            boolean runOnVirtualThread = isRunOnVirtualThread(currentMethodInfo, blocking, defaultBlocking);
            // we want to allow "overriding" the blocking/non-blocking setting from an implementation class
            // when the class defining the annotations is an interface
            if (!actualEndpointInfo.equals(currentClassInfo) && Modifier.isInterface(currentClassInfo.flags())) {
                MethodInfo actualMethodInfo = actualEndpointInfo.method(currentMethodInfo.name(),
                        currentMethodInfo.parameterTypes().toArray(new Type[0]));
                if (actualMethodInfo != null) {
                    //we don't pass AUTOMATIC here, as the method signature would be the same, so the same determination
                    //would be reached for a default
                    blocking = isBlocking(actualMethodInfo,
                            blocking ? BlockingDefault.BLOCKING : BlockingDefault.NON_BLOCKING);
                    runOnVirtualThread = isRunOnVirtualThread(actualMethodInfo, blocking,
                            blocking ? BlockingDefault.BLOCKING : BlockingDefault.NON_BLOCKING);
                }
            }

            if (returnsMultipart && !blocking) {
                throw new DeploymentException(
                        String.format(
                                "Endpoints that produce a Multipart result can only be used on blocking methods. Offending method is '%s#%s'",
                                currentMethodInfo.declaringClass().name(), currentMethodInfo));
            }

            methodContext.put(METHOD_PRODUCES, produces);
            ResourceMethod method = createResourceMethod(currentMethodInfo, actualEndpointInfo, methodContext)
                    .setHttpMethod(httpMethod == null ? null : httpAnnotationToMethod.get(httpMethod))
                    .setPath(sanitizePath(methodPath))
                    .setConsumes(consumes)
                    .setProduces(produces)
                    .setNameBindingNames(nameBindingNames)
                    .setName(currentMethodInfo.name())
                    .setBlocking(blocking)
                    .setRunOnVirtualThread(runOnVirtualThread)
                    .setSuspended(suspended)
                    .setSse(sse)
                    .setEncoded(currentMethodInfo.hasDeclaredAnnotation(ENCODED))

View on GitHub (pinned to e1c734241f)

Solutions

  1. Mark the method @Blocking so it is executed on the worker thread pool
  2. Remove @NonBlocking/@RunOnVirtualThread from the method or class
  3. Restructure the endpoint to avoid multipart output if it must remain non-blocking (e.g. emit a different media type)

Example fix

// before
@GET
@Produces(MediaType.MULTIPART_FORM_DATA)
@NonBlocking
public Multi<Part> stream() { ... }
// after
@GET
@Produces(MediaType.MULTIPART_FORM_DATA)
@Blocking
public Multi<Part> stream() { ... }
Defensive patterns

Strategy: validation

Validate before calling

Method m = MyResource.class.getMethod("stream");
boolean nonBlocking = m.isAnnotationPresent(NonBlocking.class)
    || MyResource.class.isAnnotationPresent(NonBlocking.class);
Produces produces = m.getAnnotation(Produces.class);
boolean multipart = produces != null && java.util.Arrays.stream(produces.value())
    .anyMatch(mt -> mt.contains("multipart"));
if (multipart && nonBlocking) throw new IllegalStateException("Multipart-producing endpoint must be blocking");

Prevention

When it happens

Trigger: A resource method returns a type that produces a multipart response (e.g. Multi<Part>, MultipartResult, or annotated to produce multipart/form-data) while the method (or its class) is marked @NonBlocking, runs on virtual threads as non-blocking, or is otherwise resolved as non-blocking.

Common situations: Adding @NonBlocking or @RunOnVirtualThread to a streaming multipart endpoint; returning a Multi<...> multipart stream after annotating a controller as reactive; changing return type to multipart without revisiting threading annotations.

Related errors


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