{"record":{"id":"5825c3b798b08680","repo":"spring-projects/spring-ai","slug":"could-not-read-content-length-5825c3","errorCode":null,"errorMessage":"Could not read content length","messagePattern":"Could not read content length","errorType":"exception","errorClass":"OpenAIIoException","httpStatus":null,"severity":"error","filePath":"models/spring-ai-openai/src/main/java/org/springframework/ai/openai/http/okhttp/SpringAiOpenAiHttpClient.java","lineNumber":349,"sourceCode":"\n\tprivate static String toBaseUrl(HttpUrl url) {\n\t\tStringBuilder sb = new StringBuilder();\n\t\tsb.append(url.scheme()).append(\"://\").append(url.host());\n\t\tif (url.port() != HttpUrl.defaultPort(url.scheme())) {\n\t\t\tsb.append(':').append(url.port());\n\t\t}\n\t\treturn sb.toString();\n\t}\n\n\tprivate static HttpRequestBody toHttpRequestBody(RequestBody source) {\n\t\tfinal MediaType mediaType = source.contentType();\n\t\tfinal String mediaTypeString = mediaType != null ? mediaType.toString() : null;\n\t\tfinal long length;\n\t\ttry {\n\t\t\tlength = source.contentLength();\n\t\t}\n\t\tcatch (IOException e) {\n\t\t\tthrow new OpenAIIoException(\"Could not read content length\", e);\n\t\t}\n\t\tfinal boolean isOneShot = source.isOneShot();\n\n\t\treturn new HttpRequestBody() {\n\t\t\t@Override\n\t\t\tpublic @Nullable String contentType() {\n\t\t\t\treturn mediaTypeString;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic long contentLength() {\n\t\t\t\treturn length;\n\t\t\t}\n\n\t\t\t@Override\n\t\t\tpublic boolean repeatable() {\n\t\t\t\treturn !isOneShot;\n\t\t\t}","sourceCodeStart":331,"sourceCodeEnd":367,"githubUrl":"https://github.com/spring-projects/spring-ai/blob/98a7beda4f29d80a71c5837eb4053b03a93a46f7/models/spring-ai-openai/src/main/java/org/springframework/ai/openai/http/okhttp/SpringAiOpenAiHttpClient.java#L331-L367","documentation":"When converting an internal request body into an OkHttp RequestBody, SpringAiOpenAiHttpClient reads the body source's content length; an IOException while querying the length is wrapped in OpenAIIoException('Could not read content length').","triggerScenarios":"Sending a request with a streaming/non-repeatable body whose source throws IOException when contentLength() is queried during toHttpRequestBody, called from toHttpRequest.","commonSituations":"Custom HttpRequestBody implementations backed by a stream that errors on length probing (e.g. a closed or pipe-backed stream), or a body whose source is consumed before the request is built.","solutions":["Check that the HttpRequestBody/stream you pass is open and readable at request-build time.","Provide a body implementation whose content length is known (byte[] or file-backed) rather than a one-shot stream.","Inspect the wrapped IOException cause to identify the failing source."],"exampleFix":"// before\nHttpRequestBody body = HttpRequestBody.fromStream(streamThatMayBeClosed);\n// after\nbyte[] bytes = stream.readAllBytes();\nHttpRequestBody body = HttpRequestBody.fromBytes(bytes);","handlingStrategy":"validation","validationCode":"// ensure the body is materialized and readable before the call\nbyte[] payload = bodyStream.readAllBytes();\nif (payload.length == 0) { throw new IllegalStateException(\"Request body is empty/closed\"); }","typeGuard":null,"tryCatchPattern":"try {\n    return client.execute(request, options);\n} catch (OpenAIIoException e) {\n    if (e.getMessage().contains(\"content length\")) {\n        throw new InvalidRequestBodyException(\"Body stream unreadable when building request\", e);\n    }\n    throw e;\n}","preventionTips":["Prefer byte[]/file-backed bodies over one-shot streams","Do not close the body stream before the request is built","Test custom HttpRequestBody implementations for repeatable length/read"],"tags":["io","http-client","request-body"],"backgroundTag":"network-request-failed","analyzedSha":"98a7beda4f29d80a71c5837eb4053b03a93a46f7","analyzedAt":"2026-09-11T14:15:49.441Z","contentChangedAt":"2026-09-11T14:15:49.441Z","schemaVersion":2},"datasetVersion":"2026-09-14T11:17:12.474Z"}