{"record":{"id":"4c80bf1201a78a72","repo":"didi/DoKit","slug":"source-null","errorCode":null,"errorMessage":"source == null","messagePattern":"source == null","errorType":"exception","errorClass":"IllegalArgumentException","httpStatus":null,"severity":"error","filePath":"Android/dokit-okhttp-v3/src/main/java/com/didichuxing/doraemonkit/okhttp_api/ByteCountBufferedSinkV3.java","lineNumber":35,"sourceCode":"/**\n * 可以设置每次写入大小的BufferedSink\n * <p>\n * Created by xiandanin on 2019-05-10 16:07\n */\npublic  class ByteCountBufferedSinkV3 implements BufferedSink {\n    private final long mByteCount;\n    private final Sink mOriginalSink;\n    private final BufferedSink mDelegate;\n\n    public ByteCountBufferedSinkV3(Sink sink, long byteCount) {\n        this.mOriginalSink = sink;\n        this.mDelegate = Okio.buffer(mOriginalSink);\n        this.mByteCount = byteCount;\n    }\n\n    @Override\n    public long writeAll(Source source) throws IOException {\n        if (source == null) throw new IllegalArgumentException(\"source == null\");\n        long totalBytesRead = 0;\n        for (long readCount; (readCount = source.read(buffer(), mByteCount)) != -1; ) {\n            totalBytesRead += readCount;\n            emitCompleteSegments();\n        }\n        return totalBytesRead;\n    }\n\n    @Override\n    public BufferedSink write(byte[] source, int offset, int byteCount) throws IOException {\n        if (!isOpen()) throw new IllegalStateException(\"closed\");\n        //计算出要写入的次数\n        long count = (long) Math.ceil((double) source.length / mByteCount);\n        for (int i = 0; i < count; i++) {\n            //让每次写入的字节数精确到mByteCount 分多次写入\n            long newOffset = i * mByteCount;\n            long writeByteCount = Math.min(mByteCount, source.length - newOffset);\n            buffer().write(source, (int) newOffset, (int) writeByteCount);","sourceCodeStart":17,"sourceCodeEnd":53,"githubUrl":"https://github.com/didi/DoKit/blob/626827cddb2feb2f3aee87a52a064b4e5ca2bed4/Android/dokit-okhttp-v3/src/main/java/com/didichuxing/doraemonkit/okhttp_api/ByteCountBufferedSinkV3.java#L17-L53","documentation":"ByteCountBufferedSinkV3 is DoraemonKit's okio BufferedSink wrapper that writes in fixed mByteCount chunks (used to intercept and mirror OkHttp request/response bodies). writeAll(Source) validates its argument up front, mirroring okio's own contract, and throws IllegalArgumentException on a null source before attempting any read.","triggerScenarios":"Calling writeAll(source) with null — typically when a response/request body Source resolved to null (empty body mapped to null, interceptor returning null for the body, or an upstream API returning null instead of an empty Source).","commonSituations":"OkHttp interceptors in DoraKit's network inspection flow where a HEAD/204 response has no body and the code passes body.source() from a null body. Third-party servers returning empty bodies handled as null. Kotlin code passing a nullable Source without a null check.","solutions":["Null-check the source and skip or substitute Buffer() (an empty Source) when null","Fix the producer: map 'no body' to an empty okio Buffer instead of null","In Kotlin, use 'source ?: Buffer()' or an explicit if-null guard"],"exampleFix":"// before\nbyteCountSink.writeAll(nullableSource);\n\n// after\nif (nullableSource != null) {\n  byteCountSink.writeAll(nullableSource);\n} else {\n  byteCountSink.write(Buffer()); // or simply skip\n}","handlingStrategy":"validation","validationCode":"if (source != null) { byteCountSink.writeAll(source); } else { byteCountSink.write(new okio.Buffer()); }","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Map 'no body' to an empty Buffer, not null, in interceptor code","Skip capture entirely for bodyless requests/responses (HEAD, 204)"],"tags":["okio","okhttp","doraemonkit","null-check","java"],"backgroundTag":null,"analyzedSha":"626827cddb2feb2f3aee87a52a064b4e5ca2bed4","analyzedAt":"2026-08-14T12:45:58.758Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}