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
- Use a fresh HttpURLConnection (url.openConnection()) for each request/retry attempt.
- Write the entire body before touching any response API; never call getOutputStream() twice.
- 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
- One connection, one request: never reuse a HttpURLConnection for a second attempt.
- Finish all body writes before calling any response-reading API.
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
- Cannot access request header fields after connection is set
- method does not support a request body: ${method}
- ${method} does not support writing
- Cannot set request property after connection is made
- Cannot add request property after connection is made
AI-assisted analysis of didi/DoKit@626827cddb (2026-08-14).
Data as JSON: /api/errors/2df890d6800bd2cc.
Report an issue: GitHub.