spring-projects/spring-ai · error · AnthropicIoException

Could not read content length

Error message

Could not read content length

What it means

When converting an okio BufferedSource into an OkHttp RequestBody (toHttpRequestBody), the client calls source.contentLength(), which can throw IOException for sources whose length is not directly readable. That IOException is wrapped in AnthropicIoException 'Could not read content length'.

Source

Thrown at models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/http/okhttp/SpringAiAnthropicHttpClient.java:399

	private static String toBaseUrl(HttpUrl url) {
		StringBuilder sb = new StringBuilder();
		sb.append(url.scheme()).append("://").append(url.host());
		if (url.port() != HttpUrl.defaultPort(url.scheme())) {
			sb.append(':').append(url.port());
		}
		return sb.toString();
	}

	private static HttpRequestBody toHttpRequestBody(RequestBody source) {
		final MediaType mediaType = source.contentType();
		final String mediaTypeString = mediaType != null ? mediaType.toString() : null;
		final long length;
		try {
			length = source.contentLength();
		}
		catch (IOException e) {
			throw new AnthropicIoException("Could not read content length", e);
		}
		final boolean isOneShot = source.isOneShot();

		return new HttpRequestBody() {
			@Override
			public @Nullable String contentType() {
				return mediaTypeString;
			}

			@Override
			public long contentLength() {
				return length;
			}

			@Override
			public boolean repeatable() {
				return !isOneShot;
			}

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Supply request content from a replayable source: byte[] , File, or a string rather than a live stream.
  2. If streaming is required, implement/choose a body that sets a known Content-Length or uses chunked encoding.
  3. Verify the backing file/stream is open and readable before building the request.
  4. Catch AnthropicIoException and inspect the cause to identify which source failed.

Example fix

// before
requestBuilder.content(new InputStreamContent(sourceStream)); // contentLength() throws
// after
byte[] bytes = sourceStream.readAllBytes();
requestBuilder.content(new ByteArrayContent(bytes));
Defensive patterns

Strategy: validation

Validate before calling

static byte[] toBytes(InputStream in) throws IOException {
    return in.readAllBytes(); // materialize before building the request
}

Type guard

static long safeContentLength(Source src) {
    try { return src.contentLength(); }
    catch (IOException e) { throw new IllegalStateException("Source cannot report length; buffer it first", e); }
}

Try / catch

try {
    return client.sendRequest(request);
} catch (AnthropicIoException e) {
    if ("Could not read content length".equals(e.getMessage())) {
        // rebuild request with buffered byte[] content
    }
    throw e;
}

Prevention

When it happens

Trigger: Sending a request whose body is backed by a stream/source that cannot report its length (e.g. a non-replayable streaming source, a file being concurrently modified, or a source already consumed) while building the outgoing HTTP request.

Common situations: Uploading a file that was deleted or truncated mid-request; passing a streaming Source (e.g. from a socket or pipe) as request content; double-consuming a request body source.

Understand the failure class

Background: "failed to read file", EACCES, ENOENT and "could not read <path>" errors: when a program can't read a file from disk — this error's family across 49 libraries.

Related errors


AI-assisted analysis of spring-projects/spring-ai@98a7beda4f (2026-09-11). Data as JSON: /api/errors/7e2e9e0a654c0d61. Report an issue: GitHub.