jhy/jsoup · error · IOException

Cannot follow redirect with a streamed request body…

Error message

Cannot follow redirect with a streamed request body; disable followRedirects and resend with a fresh stream

What it means

When followRedirects is enabled and a redirect arrives, jsoup cannot replay a request whose body is a live InputStream (or a multipart upload), since the stream has already been consumed. It throws IOException telling you to disable redirects and send the body again yourself with a fresh stream.

Solutions

  1. Disable redirects: request.followRedirects(false), catch the redirect response, then resend manually with a fresh InputStream
  2. Buffer the body as a byte[] (requestBody(byte[]) or requestBody(String)) so it can be replayed across redirects
  3. If the redirect should change to GET (302/303), ensure the server responds with a method-changing status so the body is dropped

Example fix

// before
Connection.Response res = Jsoup.connect(url)
    .requestBody(new FileInputStream(file))
    .execute(); // throws on redirect
// after
Connection conn = Jsoup.connect(url)
    .requestBody(new FileInputStream(file))
    .followRedirects(false);
Connection.Response res = conn.execute();
if (res.statusCode() / 100 == 3) {
    res = Jsoup.connect(res.header("Location"))
        .requestBody(new FileInputStream(file)) // fresh stream
        .execute();
}
Defensive patterns

Strategy: try-catch

Validate before calling

boolean risky = req.requestBody() instanceof InputStream || usesMultipart(req); if (risky && req.followRedirects()) { /* restructure: disable redirects */ }

Try / catch

try { res = conn.execute(); } catch (IOException e) { if (e.getMessage().contains("streamed request body")) { /* resend manually with fresh stream */ } }

Prevention

When it happens

Trigger: Executing a POST/PUT whose body was set via requestBody(InputStream) or via data requiring multipart, with followRedirects(true) (the default), and the server replies 301/302/307/308 keeping the same method.

Common situations: File-upload endpoints behind redirects; APIs moved to new hosts returning 307; retry logic resuming an upload session where the first request redirects.

Understand the failure class

Background: "API error: {status}" and "HTTP 401/403/404/429/5xx" errors: non-2xx HTTP responses explained — this error's family across 27 libraries.

Related errors


AI-assisted analysis of jhy/jsoup@9851ac5d9c (2026-09-08). Data as JSON: /api/errors/dc9d5cf5824c9eb3. Report an issue: GitHub.

Appendix: source

Thrown at src/main/java/org/jsoup/helper/HttpConnection.java:931

            if (!supportsBody)
                Validate.isFalse(hasBody, "Cannot set a request body for HTTP method " + req.method());

            // set up the request for execution
            if (!req.data().isEmpty() && (!supportsBody || hasBody))
                serialiseRequestUrl(req);
            else if (supportsBody)
                setOutputContentType(req);

            long startTime = System.nanoTime();
            RequestExecutor executor = RequestDispatch.get(req, prevRes);
            Response res = null;
            try {
                res = executor.execute();

                Method nextMethod = redirectMethod(res.statusCode, req.method());
                if (nextMethod != null && res.hasHeader(LOCATION) && req.followRedirects()) {
                    if (nextMethod == req.method() && (req.body instanceof InputStream || needsMultipart(req)))
                        throw new IOException("Cannot follow redirect with a streamed request body; disable followRedirects and resend with a fresh stream");

                    if (nextMethod != req.method()) {
                        req.method(nextMethod);
                        req.data().clear();
                        req.requestBody(null);
                        for (String header : REDIRECT_CONTENT_HEADERS)
                            req.removeHeader(header);
                    }

                    String location = res.header(LOCATION);
                    Validate.notNull(location);
                    URL redir = StringUtil.resolve(req.url(), location);
                    if (!sameOrigin(req.url(), redir)) {
                        // remove sensitive headers; defense-in-depth against open redirects
                        req.removeHeader("Authorization");
                        req.removeHeader("Cookie");
                        req.removeHeader("Cookie2");
                        req.cookies().clear();

View on GitHub (pinned to 9851ac5d9c)