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
- URL-encode dynamic path/query segments before inserting them (e.g. `URLEncoder.encode(value, StandardCharsets.UTF_8)`).
- Validate the final URL: `new URI(url)` in a pre-check, or run the request through `createUri`'s encoding first.
- Fix the scheme/host typos in the HTTP task configuration.
- 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
- Always URLEncoder.encode dynamic path and query segments.
- Never concatenate raw user/process-variable values into URLs.
- Validate URLs at configuration time, not request time.
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
- Path can not be null or empty. Syntax:
- Path can not be null or empty. Syntax:
- A channel key detection value is required for the channel…
- A group or a user is required to create an identity link.
- A group or a user is required to create an identity link.
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)