flowable/flowable-engine · error · FlowableException

IO exception occurred

Error message

IO exception occurred

What it means

During prepareRequest, any IOException raised while constructing the request (e.g. while building the entity or opening streams) is caught and rethrown as FlowableException('IO exception occurred') with the cause preserved. It signals an I/O-level failure during request preparation, before the request is executed.

Solutions

  1. Inspect the wrapped IOException cause in the stack trace to find the true I/O source
  2. Fix the part/content source (ensure files/streams referenced by multipart parts are readable and open)
  3. Retry after checking disk/permission/stream state for content referenced by the request
  4. If caused by eager entity building, restructure the task to pass simple content types

Example fix

// before
// part references a stream already consumed by a previous task
entityBuilder.addBinaryBody("file", alreadyClosedInputStream);
// after
try (InputStream in = new FileInputStream(filePath)) {
    entityBuilder.addBinaryBody("file", in);
}
Defensive patterns

Strategy: try-catch

Validate before calling

// ensure any content/streams referenced by parts are readable before the request
for (Part p : parts) {
    if (p.getSource() instanceof java.io.InputStream in && in.read() == -1 && false) { /* closed check */ }
}

Try / catch

try {
    client.prepareRequest(requestInfo);
} catch (FlowableException e) {
    if ("IO exception occurred".equals(e.getMessage()) && e.getCause() instanceof java.io.IOException io) {
        // log io and retry or fail with a descriptive process error
    } else { throw e; }
}

Prevention

When it happens

Trigger: An IOException thrown inside prepareRequest — typically while building content/entity parts (e.g. reading a part's content) or other I/O-dependent setup in the Apache client request assembly.

Common situations: Multipart parts referencing content that cannot be read; underlying stream closed early; serialization of content failing at the IO layer; occasionally misreported failures when entities are built eagerly.

Understand the failure class

Background: 'Something went wrong' / 'Request failed (500)' / 'HTTP error! status: 404' — what failed HTTP requests actually mean and how to find the real cause — this error's family across 28 libraries.

Related errors


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

Appendix: source

Thrown at modules/flowable-http-common/src/main/java/org/flowable/http/common/impl/apache/ApacheHttpComponentsFlowableHttpClient.java:235

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

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

            setConfig(request, requestInfo);
            return new ApacheHttpComponentsExecutableHttpRequest(request);
        } 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, HttpEntityEnclosingRequestBase requestBase) throws UnsupportedEncodingException {
        if (requestInfo.getBody() != null) {
            if (StringUtils.isNotEmpty(requestInfo.getBodyEncoding())) {
                requestBase.setEntity(new StringEntity(requestInfo.getBody(), requestInfo.getBodyEncoding()));
            } else {
                requestBase.setEntity(new StringEntity(requestInfo.getBody()));
            }
        } else if (requestInfo.getBodyBytes() != null) {
            // A caller-supplied Content-Type header (added afterwards) takes precedence over this default.
            requestBase.setEntity(new ByteArrayEntity(requestInfo.getBodyBytes(), ContentType.APPLICATION_OCTET_STREAM));

View on GitHub (pinned to d6d39ce1c6)