{"record":{"id":"31f897bf5976284b","repo":"didi/DoKit","slug":"expected-expectedcontentlength-bytes-but-r","errorCode":null,"errorMessage":"expected \" + expectedContentLength + \" bytes but received \" + bytesReceived + byteCount","messagePattern":"expected \" \\+ expectedContentLength \\+ \" bytes but received \" \\+ bytesReceived \\+ byteCount","errorType":"exception","errorClass":"ProtocolException","httpStatus":null,"severity":"error","filePath":"Android/dokit/src/main/java/com/didichuxing/doraemonkit/aop/urlconnection/ObsoleteUrlFactory.java","lineNumber":836,"sourceCode":"            this.timeout = sink.timeout();\n            this.expectedContentLength = expectedContentLength;\n\n            // An output stream that writes to sink. If expectedContentLength is not -1, then this expects\n            // exactly that many bytes to be written.\n            this.outputStream = new OutputStream() {\n                private long bytesReceived;\n\n                @Override\n                public void write(int b) throws IOException {\n                    write(new byte[]{(byte) b}, 0, 1);\n                }\n\n                @Override\n                public void write(byte[] source, int offset, int byteCount) throws IOException {\n                    if (closed) throw new IOException(\"closed\"); // Not IllegalStateException!\n\n                    if (expectedContentLength != -1L && bytesReceived + byteCount > expectedContentLength) {\n                        throw new ProtocolException(\"expected \" + expectedContentLength\n                                + \" bytes but received \" + bytesReceived + byteCount);\n                    }\n\n                    bytesReceived += byteCount;\n                    try {\n                        sink.write(source, offset, byteCount);\n                    } catch (InterruptedIOException e) {\n                        throw new SocketTimeoutException(e.getMessage());\n                    }\n                }\n\n                @Override\n                public void flush() throws IOException {\n                    if (closed) return; // Weird, but consistent with historical behavior.\n                    sink.flush();\n                }\n\n                @Override","sourceCodeStart":818,"sourceCodeEnd":854,"githubUrl":"https://github.com/didi/DoKit/blob/626827cddb2feb2f3aee87a52a064b4e5ca2bed4/Android/dokit/src/main/java/com/didichuxing/doraemonkit/aop/urlconnection/ObsoleteUrlFactory.java#L818-L854","documentation":"In fixed-length mode (expectedContentLength != -1, set via setFixedLengthStreamingMode), the body OutputStream counts written bytes and throws ProtocolException(\"expected N bytes but received M\") as soon as writes would exceed the declared length. The declared content length and the actual bytes written must match exactly.","triggerScenarios":"Calling setFixedLengthStreamingMode(n) but writing more than n bytes — e.g. declaring file.length() then adding headers/boundaries on top, or writing the body twice.","commonSituations":"Multipart uploads where the declared length omits boundary bytes; declaring a compressed/inflated length different from the actual bytes; off-by-one or duplicated chunk writes.","solutions":["Recompute the declared length to include every byte (boundaries, CRLFs, headers inside the body) — build the body into a ByteArrayOutputStream first and use its size().","If the exact size is hard to know, switch to setChunkedStreamingMode().","Write the body once from a single code path to avoid duplicate writes."],"exampleFix":"// before\nconn.setFixedLengthStreamingMode(file.length());\nos.write(multipartHeader); os.write(fileBytes); // exceeds file.length()\n\n// after\nbyte[] body = buildMultipart(file); // header+file+boundary\nconn.setFixedLengthStreamingMode(body.length);\nos.write(body);","handlingStrategy":"validation","validationCode":"byte[] body = buildBody(); // exact bytes you will write\nconn.setFixedLengthStreamingMode(body.length);\n// then write exactly body","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Buffer the full body and use its size() as the declared length.","If exact size is uncertain, use chunked mode instead of guessing."],"tags":["network","http","upload","content-length"],"backgroundTag":null,"analyzedSha":"626827cddb2feb2f3aee87a52a064b4e5ca2bed4","analyzedAt":"2026-08-14T12:45:58.758Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}