didi/DoKit · error · ProtocolException

cannot write request body after response has been read

Error message

cannot write request body after response has been read

What it means

getOutputStream() checks requestBody.closed after optionally proceeding with a streamed request; if the request body was already closed (which happens once the response has been read or the stream closed), writing is rejected with ProtocolException("cannot write request body after response has been read"). This prevents mutating a request that OkHttp has already sent.

Source

Thrown at Android/dokit/src/main/java/com/didichuxing/doraemonkit/aop/urlconnection/ObsoleteUrlFactory.java:485

                return ConvertUtils.string2InputStream("response.body() is null:" + url.toString(), "utf-8");
            }

        }

        @Override
        public OutputStream getOutputStream() throws IOException {
            OutputStreamRequestBody requestBody = (OutputStreamRequestBody) buildCall().request().body();
            if (requestBody == null) {
                throw new ProtocolException("method does not support a request body: " + method);
            }

            if (requestBody instanceof StreamedRequestBody) {
                connect();
                networkInterceptor.proceed();
            }

            if (requestBody.closed) {
                throw new ProtocolException("cannot write request body after response has been read");
            }

            return requestBody.outputStream;
        }

        @Override
        public Permission getPermission() {
            URL url = getURL();
            String hostname = url.getHost();
            int hostPort = url.getPort() != -1
                    ? url.getPort()
                    : HttpUrl.defaultPort(url.getProtocol());
            if (usingProxy()) {
                InetSocketAddress proxyAddress = (InetSocketAddress) client.proxy().address();
                hostname = proxyAddress.getHostName();
                hostPort = proxyAddress.getPort();
            }
            return new SocketPermission(hostname + ":" + hostPort, "connect, resolve");

View on GitHub (pinned to 626827cddb)

Solutions

  1. Use a fresh HttpURLConnection (url.openConnection()) for each request/retry attempt.
  2. Write the entire body before touching any response API; never call getOutputStream() twice.
  3. Keep a boolean sent flag in wrapper code to prevent post-response writes.

Example fix

// before
OutputStream os = conn.getOutputStream();
os.write(part1); os.close();
String body = read(conn.getInputStream());
conn.getOutputStream().write(part2); // ProtocolException

// after
// send part1+part2 in one go, or open a new connection:
HttpURLConnection conn2 = (HttpURLConnection) url.openConnection();
Defensive patterns

Strategy: validation

Validate before calling

if (bodyWritten) throw new IllegalStateException("request already sent"); // own guard before writes

Prevention

When it happens

Trigger: Calling getOutputStream() a second time on the same connection after closing the first stream or after reading the response (getInputStream/getResponseCode); writing to a stream obtained before connect() after the response arrived.

Common situations: Retry logic that reuses one HttpURLConnection instance; streaming upload code that captures the OutputStream, reads the response, then tries to append more data.

Related errors


AI-assisted analysis of didi/DoKit@626827cddb (2026-08-14). Data as JSON: /api/errors/2df890d6800bd2cc. Report an issue: GitHub.