Skip to content

Fix WirespecWebClient ClassCastException on Spring Framework 7 / Spring Boot 4#671

Merged
wilmveel merged 3 commits into
masterfrom
claude/affectionate-ritchie-oi8e7u
Jun 19, 2026
Merged

Fix WirespecWebClient ClassCastException on Spring Framework 7 / Spring Boot 4#671
wilmveel merged 3 commits into
masterfrom
claude/affectionate-ritchie-oi8e7u

Conversation

@nsmnds

@nsmnds nsmnds commented Jun 18, 2026

Copy link
Copy Markdown
Contributor

Summary

WirespecWebClient (both the Kotlin and Java reactive clients) threw a ClassCastException on every response when running on Spring Framework 7 / Spring Boot 4:

java.lang.ClassCastException: class org.springframework.http.ReadOnlyHttpHeaders
cannot be cast to class java.util.Map

Root cause

The clients converted downstream response headers into the Map<String, List<String>> expected by Wirespec.RawResponse by passing Spring's HttpHeaders straight into CollectionUtils.toMultiValueMap(...), which relies on HttpHeaders being a Map.

  • In Spring 6.x, HttpHeaders implements MultiValueMap<String, String> (and therefore Map), so the upcast is valid and the artifact compiles cleanly.
  • In Spring 7.0 (Spring Boot 4), HttpHeaders was redesigned and no longer implements Map/MultiValueMap. The published jar — compiled against Spring 6 — therefore fails the cast at runtime on Spring 7.

Fix

Stop assuming HttpHeaders is a Map. Convert headers by iterating via HttpHeaders.forEach(BiConsumer) into a LinkedMultiValueMap, replacing the three toMultiValueMap(...) call sites in each client.

Backwards compatibility ✅

The change is binary compatible with both Spring 6 and Spring 7. forEach(BiConsumer<? super String, ? super List<String>>) exists with an identical binary descriptor (Ljava/util/function/BiConsumer;)V in both versions:

  • Spring 6 — inherited as the default method from Map
  • Spring 7 — declared directly on HttpHeaders

So a single jar compiled against Spring 6 (as this repo is, on spring-webflux 6.1.13) resolves the call correctly at runtime on Spring 7 as well. No keySet()/headerNames() (which differ between versions) is used.

Changes

  • WirespecWebClient.kt (Kotlin) — added a private HttpHeaders.toRawHeaders() helper and replaced all three toMultiValueMap(...) call sites.
  • WirespecWebClient.java (Java) — added a private static toRawHeaders(HttpHeaders) helper and replaced all three CollectionUtils.toMultiValueMap(...) call sites.

Verification

  • :src:integration:spring:compileKotlinJvm and compileJvmMainJava compile cleanly against Spring 6.1.13.
  • The existing client integration tests (*it.client.*), including ResponseHeaderCaseInsensitivityTest, which exercise the exact response-header mapping path, pass.

🤖 Generated with Claude Code


Generated by Claude Code

claude added 2 commits June 18, 2026 15:47
WirespecWebClient converted downstream response headers by passing
Spring's HttpHeaders straight into CollectionUtils.toMultiValueMap,
which relies on HttpHeaders being a Map. In Spring Framework 6
HttpHeaders implements MultiValueMap (and therefore Map), but in
Spring Framework 7 (Spring Boot 4) HttpHeaders no longer does, so a
jar compiled against Spring 6 throws:

  ClassCastException: ReadOnlyHttpHeaders cannot be cast to java.util.Map

at runtime on Spring 7 for every response.

Convert headers by iterating via HttpHeaders.forEach(BiConsumer) into a
LinkedMultiValueMap instead. That method exists with an identical binary
descriptor on both Spring 6 (inherited from Map) and Spring 7 (declared
directly), so a single jar compiled against Spring 6 keeps working on
both versions. Applied to both the Kotlin and Java WirespecWebClient.

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GFtN52uDWW7dgxwQE5kEGb
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GFtN52uDWW7dgxwQE5kEGb
@wilmveel wilmveel enabled auto-merge (squash) June 19, 2026 06:49
@wilmveel wilmveel merged commit 2dbba50 into master Jun 19, 2026
34 checks passed
@wilmveel wilmveel deleted the claude/affectionate-ritchie-oi8e7u branch June 19, 2026 07:03
@sonarqubecloud

Copy link
Copy Markdown

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants