didi/DoKit · error · ProtocolException
expected " + expectedContentLength + " bytes but received "
Error message
expected " + expectedContentLength + " bytes but received " + bytesReceived
What it means
The close() path of the fixed-length body OutputStream throws ProtocolException("expected N bytes but received M") when fewer bytes than the declared expectedContentLength were written before closing. OkHttp refuses to complete a request whose body is shorter than advertised, since the server would hang waiting for the missing bytes.
Source
Thrown at Android/dokit/src/main/java/com/didichuxing/doraemonkit/aop/urlconnection/ObsoleteUrlFactory.java:859
try {
sink.write(source, offset, byteCount);
} catch (InterruptedIOException e) {
throw new SocketTimeoutException(e.getMessage());
}
}
@Override
public void flush() throws IOException {
if (closed) return; // Weird, but consistent with historical behavior.
sink.flush();
}
@Override
public void close() throws IOException {
closed = true;
if (expectedContentLength != -1L && bytesReceived < expectedContentLength) {
throw new ProtocolException("expected " + expectedContentLength
+ " bytes but received " + bytesReceived);
}
sink.close();
}
};
}
@Override
public long contentLength() {
return expectedContentLength;
}
@Override
public final @Nullable
MediaType contentType() {
return null; // Let the caller provide this in a regular header.
}View on GitHub (pinned to 626827cddb)
Solutions
- Pad or recompute: build the full body first and pass its exact size to setFixedLengthStreamingMode.
- If the source can come up short, use chunked mode instead of fixed-length.
- Fail fast on short reads from the source stream instead of silently closing.
Example fix
// before conn.setFixedLengthStreamingMode(file.length()); copyMaybeShort(source, os); os.close(); // underflow -> ProtocolException // after conn.setChunkedStreamingMode(0); // length not guaranteed // or: verify copiedCount == file.length() before close, else abort
Defensive patterns
Strategy: validation
Validate before calling
long declared = body.length; // or file.length()
// after copying, before close:
if (bytesCopied != declared) throw new IOException("short body: " + bytesCopied + "/" + declared);
os.close(); Prevention
- Verify copied-byte count equals the declared length before close().
- Treat short reads from the source stream as errors, not as end-of-body.
When it happens
Trigger: setFixedLengthStreamingMode(n) then writing fewer than n bytes (early return, exception swallowed mid-write, truncated source) and calling close().
Common situations: Upload interrupted by an exception that is caught and skipped; reading from a source that returns less data than its reported length (file modified concurrently); conditional writes that skip the last chunk.
Related errors
- expected " + expectedContentLength + " bytes but received "
- Cannot access request header fields after connection is set
- This protocol does not support input
- method does not support a request body: ${method}
- cannot write request body after response has been read
AI-assisted analysis of didi/DoKit@626827cddb (2026-08-14).
Data as JSON: /api/errors/616d8137eeff3875.
Report an issue: GitHub.