Skip to content

Fix NPE in Gson when serializing request headers on cold start#1603

Merged
cortinico merged 1 commit into
ChuckerTeam:mainfrom
faraz152:fix/1602-gson-header-npe
Apr 23, 2026
Merged

Fix NPE in Gson when serializing request headers on cold start#1603
cortinico merged 1 commit into
ChuckerTeam:mainfrom
faraz152:fix/1602-gson-header-npe

Conversation

@faraz152

@faraz152 faraz152 commented Apr 8, 2026

Copy link
Copy Markdown
Contributor

Summary

Root cause

On cold start (after long idle), Gson's internal reflection for List<HttpHeader> type resolution triggers a NullPointerException in libcore.reflect.TypeVariableImpl.hashCode(). This is a known JVM/Android runtime edge case where type metadata hasn't been fully initialized.

Changes

  • HttpTransaction.kt — Added defensive try-catch around both setRequestHeaders and setResponseHeaders serialization calls
  • On failure, falls back to plain-text header representation (name: value per line) so the transaction is still recorded

Test plan

  • Existing unit tests pass (testDebugUnitTest)
  • Build compiles successfully
  • Fallback produces readable header output instead of crashing
  • Manual: Install with Chucker after long idle → first request should not crash

🤖 Generated with Claude Code

@faraz152 faraz152 requested a review from a team as a code owner April 8, 2026 12:35

@cortinico cortinico left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Thanks for looking into this @faraz152 - there are a couple of things to fix though

try {
JsonConverter.instance.toJson(headers)
} catch (@Suppress("TooGenericExceptionCaught") e: Exception) {
headers.joinToString(separator = "\n") { "${it.name}: ${it.value}" }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This won't work sadly. The caller will always assume the field contains a JSON string:

fun getParsedRequestHeaders(): List<HttpHeader>? =
JsonConverter.instance.fromJson<List<HttpHeader>>(
requestHeaders,
TypeToken.getParameterized(List::class.java, HttpHeader::class.java).type,
)

So you should store it as valid JSON, not as plain string.

What would happen is that you will show nothing to the user even if headers are there.

try {
JsonConverter.instance.toJson(headers)
} catch (@Suppress("TooGenericExceptionCaught") e: Exception) {
headers.joinToString(separator = "\n") { "${it.name}: ${it.value}" }

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Once you do the other changes, can you refactor so that this code is inside a util function + add tests?

@faraz152

Copy link
Copy Markdown
Contributor Author

Thanks @cortinico, good catch. Pushed an update that addresses both points:

  • The fallback no longer stores a plain name: value string. It now builds a real JSON array via Gson's JsonArray/JsonObject tree API (which skips the reflective element-type resolution that was blowing up), so getParsedRequestHeaders() / getParsedResponseHeaders() still round-trip correctly.
  • Extracted the logic into HttpHeaderSerializer in internal/support and added HttpHeaderSerializerTest covering: default path round-trip, empty list, fallback round-trip, and fallback escaping of quotes/control chars.
  • ktlint + detekt are clean locally.

@faraz152

Copy link
Copy Markdown
Contributor Author

@cortinico — CI is also showing action_required here (needs a maintainer to approve the workflow run). Let me know if you'd like any changes to the HttpHeaderSerializer implementation while you're reviewing.

Wrap `JsonConverter.instance.toJson(headers)` in try-catch in both
`setRequestHeaders` and `setResponseHeaders` to prevent NPE from
crashing the request thread when Gson's TypeVariableImpl.hashCode()
fails during cold start reflection.

On failure, falls back to a plain-text representation of the headers
so the transaction is still recorded without crashing the app.

Fixes ChuckerTeam#1602

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@faraz152 faraz152 force-pushed the fix/1602-gson-header-npe branch from 302e09d to d3f19bb Compare April 23, 2026 08:59
@cortinico cortinico enabled auto-merge (squash) April 23, 2026 23:12
@cortinico cortinico merged commit ac7a09a into ChuckerTeam:main Apr 23, 2026
6 checks passed
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.

NPE in TypeVariableImpl.hashCode when serializing request headers (Gson) on cold start

2 participants