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

  1. Pad or recompute: build the full body first and pass its exact size to setFixedLengthStreamingMode.
  2. If the source can come up short, use chunked mode instead of fixed-length.
  3. 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

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


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