flowable/flowable-engine · error · FlowableIllegalArgumentException

Delegate expression " + expression + " did not resolve to an

Error message

Delegate expression " + expression + " did not resolve to an implementation of " + HttpRequestHandler.class

What it means

Thrown as FlowableIllegalArgumentException when a delegate expression configured for an HTTP request handler (flowable:class / delegateExpression on the HTTP task request handler) resolves to an object that does not implement org.flowable.http.HttpRequestHandler. Flowable requires the resolved delegate to be an HttpRequestHandler so it can wrap it in HttpRequestHandlerInvocation and route the HTTP request handling through the delegate interceptor. This is a configuration/typing contract check.

Source

Thrown at modules/flowable-engine/src/main/java/org/flowable/engine/impl/bpmn/http/handler/DelegateExpressionHttpHandler.java:51

 */
public class DelegateExpressionHttpHandler implements HttpRequestHandler, HttpResponseHandler {

    protected Expression expression;
    protected final List<FieldDeclaration> fieldDeclarations;

    public DelegateExpressionHttpHandler(Expression expression, List<FieldDeclaration> fieldDeclarations) {
        this.expression = expression;
        this.fieldDeclarations = fieldDeclarations;
    }

    @Override
    public void handleHttpRequest(VariableContainer execution, HttpRequest httpRequest, FlowableHttpClient client) {
        Object delegate = DelegateExpressionUtil.resolveDelegateExpression(expression, execution, fieldDeclarations);
        if (delegate instanceof HttpRequestHandler) {
            CommandContextUtil.getProcessEngineConfiguration().getDelegateInterceptor().handleInvocation(
                            new HttpRequestHandlerInvocation((HttpRequestHandler) delegate, execution, httpRequest, client));
        } else {
            throw new FlowableIllegalArgumentException("Delegate expression " + expression + " did not resolve to an implementation of " + HttpRequestHandler.class);
        }
    }

    @Override
    public void handleHttpResponse(VariableContainer execution, HttpResponse httpResponse) {
        Object delegate = DelegateExpressionUtil.resolveDelegateExpression(expression, execution, fieldDeclarations);
        if (delegate instanceof HttpResponseHandler) {
            CommandContextUtil.getProcessEngineConfiguration().getDelegateInterceptor().handleInvocation(
                            new HttpResponseHandlerInvocation((HttpResponseHandler) delegate, execution, httpResponse));
        } else {
            throw new FlowableIllegalArgumentException("Delegate expression " + expression + " did not resolve to an implementation of " + HttpResponseHandler.class);
        }
    }

    /**
     * returns the expression text for this execution listener. Comes in handy if you want to check which listeners you already have.
     */
    public String getExpressionText() {

View on GitHub (pinned to d6d39ce1c6)

Solutions

  1. Make the class the expression points to implement org.flowable.http.HttpRequestHandler (handleHttpRequest method).
  2. Check the delegateExpression value in the BPMN XML to confirm it resolves to the intended bean and not another bean with a similar name.
  3. If the object only needs to be a listener, use the appropriate listener configuration instead of the HTTP handler delegateExpression.
  4. Add a startup-time check in your app (e.g. a Spring test that resolves each handler bean and asserts instanceof HttpRequestHandler) to fail fast.

Example fix

// before
public class MyHandler { public void handle(VariableContainer c) { ... } }

// after
import org.flowable.http.HttpRequestHandler;
public class MyHandler implements HttpRequestHandler {
    public void handleHttpRequest(VariableContainer execution, HttpRequest request, FlowableHttpClient client) { ... }
}
Defensive patterns

Strategy: type-guard

Validate before calling

Object delegate = DelegateExpressionUtil.resolveDelegateExpression(expression, execution);
if (!(delegate instanceof HttpRequestHandler)) {
    throw new IllegalStateException("Bean for " + expression + " must implement HttpRequestHandler");
}

Type guard

boolean isValidHttpRequestDelegate(Object o) {
    return o instanceof org.flowable.http.HttpRequestHandler;
}

Try / catch

try {
    delegateHandler.handleHttpRequest(execution, request, client);
} catch (FlowableIllegalArgumentException e) {
    if (e.getMessage().contains("did not resolve to an implementation of")) {
        logger.error("HTTP request handler delegate misconfigured: " + e.getMessage());
    } else { throw e; }
}

Prevention

When it happens

Trigger: A BPMN HTTP task defines flowable:delegateExpression (e.g. ${myHandlerBean}) for the request handler, the expression is evaluated at request-handling time via DelegateExpressionUtil.resolveDelegateExpression, and the returned object fails the `instanceof HttpRequestHandler` check.

Common situations: Pointing the delegate expression at a Spring bean that is an ExecutionListener or plain class instead of HttpRequestHandler; typo/EL resolving to the wrong bean; a bean refactored to no longer implement HttpRequestHandler after a Flowable version change; expression accidentally resolving to null or to a String.

Understand the failure class

Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.

Related errors


AI-assisted analysis of flowable/flowable-engine@d6d39ce1c6 (2026-09-11). Data as JSON: /api/errors/248f556f50fe0444. Report an issue: GitHub.