flowable/flowable-engine · error · FlowableException

Failed to read body

Error message

Failed to read body

What it means

When converting the Apache HttpClient 5 response into Flowable's `FlowableHttpResponseInfo`, the client re-reads a buffered body via `EntityUtils.toString(...)` to produce the String body while keeping the raw bytes. If that conversion throws `IOException` or `ParseException`, `toFlowableHttpResponse` throws this `FlowableException` (note: the original exception is not chained).

Solutions

  1. Check server/proxy logs for truncated responses; a mid-body disconnect is the most common cause.
  2. Fix the response's Content-Type header on the server side — an invalid charset parameter makes EntityUtils throw ParseException.
  3. Retry the request if the failure is a transient network truncation.
  4. If the exception lacks a cause, add server-side response validation/capture (or a proxy like tcpdump/Wireshark) to see the raw malformed response.
Defensive patterns

Strategy: retry

Validate before calling

// no caller-side pre-check possible for server response malformation;
// validate response after the fact instead
if (response != null && response.getStatusCode() >= 200 && response.getStatusCode() < 300 && response.getBody() == null) {
    log.warn("2xx response returned without a readable body");
}

Try / catch

try {
    return client.call(requestInfo);
} catch (FlowableException e) {
    if ("Failed to read body".equals(e.getMessage()) && attempt < maxRetries) {
        return retryWithBackoff(attempt + 1); // truncated responses are often transient
    }
    throw e;
}

Prevention

When it happens

Trigger: `toFlowableHttpResponse()` encounters an `IOException` or `ParseException` while calling `EntityUtils.toString` on the response body — e.g. a truncated/chunk-decoding error or a Content-Type header `EntityUtils` cannot parse.

Common situations: Server closes the connection mid-body (truncated chunked transfer); malformed Content-Type headers (bad charset parameter) triggering ParseException; proxies cutting responses short.

Understand the failure class

Background: "Invalid JSON response" and "Failed to parse response" errors: when an API answers 200 but the body isn't the JSON your library expected — 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/1af991421ed0c526. Report an issue: GitHub.

Appendix: source

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

            headers.add(header.getName(), header.getValue());
        }

        responseInfo.setHttpHeaders(headers);

        SimpleBody body = response.getBody();
        if (body != null) {
            if (body.isText()) {
                responseInfo.setBody(body.getBodyText());
                responseInfo.setBodyBytes(body.getBodyBytes());
            } else {
                try {
                    // We are creating a fake entity in order to rely on the creation of a String using the EntityUtils
                    // They contain some special logic for picking the default charset based on the content type
                    // (in case the content type doesn't have a charset)
                    responseInfo.setBody(EntityUtils.toString(new ByteArrayEntity(body.getBodyBytes(), body.getContentType())));
                    responseInfo.setBodyBytes(body.getBodyBytes());
                } catch (IOException | ParseException e) {
                    throw new FlowableException("Failed to read body");
                }
            }
        }

        return responseInfo;

    }

    protected class ApacheHttpComponentsExecutableHttpRequest implements AsyncExecutableHttpRequest {

        protected final AsyncRequestProducer request;
        protected final RequestConfig requestConfig;

        public ApacheHttpComponentsExecutableHttpRequest(AsyncRequestProducer request, RequestConfig requestConfig) {
            this.request = request;
            this.requestConfig = requestConfig;
        }

View on GitHub (pinned to d6d39ce1c6)