flowable/flowable-engine · error · FlowableException

Invalid URL exception occurred

Error message

Invalid URL exception occurred

What it means

`prepareRequest` in the HttpClient 5.x client converts the configured URL into a `java.net.URI` (via `createUri`, which pre-encodes spaces and pluses). A `URISyntaxException` from that conversion is wrapped as `FlowableException("Invalid URL exception occurred", cause)` — the URL string is not a syntactically valid URI.

Solutions

  1. URL-encode dynamic path/query segments before inserting them (e.g. `URLEncoder.encode(value, StandardCharsets.UTF_8)`).
  2. Validate the final URL: `new URI(url)` in a pre-check, or run the request through `createUri`'s encoding first.
  3. Fix the scheme/host typos in the HTTP task configuration.
  4. Log the exact URL from `requestInfo` — the wrapped cause's message shows the offending character and index.

Example fix

// before
String url = baseUrl + "/search?q=" + query;

// after
String url = baseUrl + "/search?q=" + URLEncoder.encode(query, StandardCharsets.UTF_8);
Defensive patterns

Strategy: validation

Validate before calling

boolean isValidUrl(String url) {
    try {
        URI uri = new URI(url.replace(" ", "%20"));
        return uri.getScheme() != null && uri.getHost() != null;
    } catch (URISyntaxException e) {
        return false;
    }
}
// call before invoking the client; encode query values with URLEncoder first

Try / catch

try {
    return client.call(requestInfo);
} catch (FlowableException e) {
    if ("Invalid URL exception occurred".equals(e.getMessage())) {
        log.error("URL '{}' is not a valid URI: {}", requestInfo.getUrl(), e.getCause());
    }
    throw e;
}

Prevention

When it happens

Trigger: Calling `prepareRequest()` with a `requestInfo` URL containing illegal URI characters (raw spaces beyond the pre-encoded ones, unencoded `|`, `{`, `}`, `<`, `>`, quotes, or a malformed scheme like "htp://").

Common situations: Building URLs by string concatenation with unencoded query values (unescaped special characters); config values with stray whitespace/newlines; template variables expanded to values containing reserved characters.

Understand the failure class

Background: "Invalid URL" errors: why new URL(), URI.parse, and reqwest::Url reject your string — missing scheme, whitespace, and bad path format — this error's family across 39 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable-http-common/src/main/java/org/flowable/http/common/impl/apache/client5/ApacheHttpComponents5FlowableHttpClient.java:232

                case "HEAD": {
                    request = AsyncRequestBuilder.head(uri);
                    break;
                }
                case "OPTIONS": {
                    request = AsyncRequestBuilder.options(uri);
                    break;
                }
                default: {
                    throw new FlowableException(requestInfo.getMethod() + " HTTP method not supported");
                }
            }

            setHeaders(request, requestInfo.getHttpHeaders());
            setHeaders(request, requestInfo.getSecureHttpHeaders());

            return new ApacheHttpComponentsExecutableHttpRequest(request.build(), createRequestConfig(requestInfo));
        } catch (URISyntaxException ex) {
            throw new FlowableException("Invalid URL exception occurred", ex);
        } catch (IOException ex) {
            throw new FlowableException("IO exception occurred", ex);
        }
    }

    protected URI createUri(String url) throws URISyntaxException {
        String uri = SPACE_CHARACTER_PATTERN.matcher(url).replaceAll(ENCODED_SPACE_CHARACTER);
        return new URI(PLUS_CHARACTER_PATTERN.matcher(uri).replaceAll(ENCODED_PLUS_CHARACTER));
    }

    protected void setRequestEntity(HttpRequest requestInfo, AsyncRequestBuilder requestBase) throws UnsupportedEncodingException {
        if (requestInfo.getBody() != null) {
            if (StringUtils.isNotEmpty(requestInfo.getBodyEncoding())) {
                requestBase.setEntity(AsyncEntityProducers.create(requestInfo.getBody(), Charset.forName(requestInfo.getBodyEncoding())));
            } else {
                requestBase.setEntity(AsyncEntityProducers.create(requestInfo.getBody()));
            }
        } else if (requestInfo.getBodyBytes() != null) {

View on GitHub (pinned to d6d39ce1c6)