spring-projects/spring-ai · error · IllegalArgumentException

httpClientBuilderCustomizers cannot be combined with a pre-b

Error message

httpClientBuilderCustomizers cannot be combined with a pre-built anthropicClientAsync because the HTTP layer is already constructed

What it means

The async counterpart of the same builder validation: build() throws IllegalArgumentException when httpClientBuilderCustomizers are set together with a pre-built AnthropicClientAsync, because the async HTTP layer is already constructed and customizers would be silently ignored.

Source

Thrown at models/spring-ai-anthropic/src/main/java/org/springframework/ai/anthropic/AnthropicChatModel.java:1950

		 */
		public Builder httpClientBuilderCustomizers(List<AnthropicHttpClientBuilderCustomizer> customizers) {
			Assert.notNull(customizers, "customizers cannot be null");
			this.httpClientCustomizers = new ArrayList<>(customizers);
			return this;
		}

		/**
		 * Builds a new {@link AnthropicChatModel} instance.
		 * @return the configured chat model
		 */
		public AnthropicChatModel build() {
			if (!this.httpClientCustomizers.isEmpty() && this.anthropicClient != null) {
				throw new IllegalArgumentException(
						"httpClientBuilderCustomizers cannot be combined with a pre-built anthropicClient "
								+ "because the HTTP layer is already constructed");
			}
			if (!this.httpClientCustomizers.isEmpty() && this.anthropicClientAsync != null) {
				throw new IllegalArgumentException(
						"httpClientBuilderCustomizers cannot be combined with a pre-built anthropicClientAsync "
								+ "because the HTTP layer is already constructed");
			}
			return new AnthropicChatModel(this.anthropicClient, this.anthropicClientAsync, this.options,
					this.toolCallingManager, this.observationRegistry, this.meterRegistry, this.dispatcherExecutor,
					this.httpClientCustomizers);
		}

	}

}

View on GitHub (pinned to 98a7beda4f)

Solutions

  1. Drop the pre-built anthropicClientAsync and let the builder construct it from baseUrl/apiKey so customizers take effect.
  2. Remove the httpClientBuilderCustomizers and configure the async client's HTTP layer directly at construction time.
  3. If you need both sync and async clients, build both inside one set of customizers by letting the builder create them.
  4. Audit your builder chain for both customizers and any anthropicClient/anthropicClientAsync calls before build().

Example fix

// before
builder.anthropicClientAsync(preBuiltAsyncClient)
       .httpClientBuilderCustomizers(customizers);
// after
builder.baseUrl("https://api.anthropic.com")
       .apiKey(apiKey)
       .httpClientBuilderCustomizers(customizers);
Defensive patterns

Strategy: validation

Validate before calling

if (customizers != null && !customizers.isEmpty() && preBuiltAsyncClient != null) {
    throw new IllegalStateException("Choose either a pre-built anthropicClientAsync OR httpClientBuilderCustomizers");
}

Try / catch

try {
    model = builder.build();
} catch (IllegalArgumentException e) {
    if (e.getMessage().contains("pre-built anthropicClientAsync")) {
        model = builderWithoutAsyncClient.build();
    } else throw e;
}

Prevention

When it happens

Trigger: Calling builder.anthropicClientAsync(asyncClient) plus builder.httpClientBuilderCustomizers(...), then build(); also triggered when both sync and async pre-built clients are set and customizers are present.

Common situations: Providing a shared async client for streaming use cases while separately configuring HTTP timeouts via customizers; copy-pasting builder configuration that sets both the client and customizers.

Related errors


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