Describe the bug
Summary
Upgrading com.datadoghq:dd-sdk-android-okhttp from 3.8.0 → 3.9.0 breaks
GraphQL subscriptions delivered over Server-Sent Events (SSE). On 3.9.0, the
DatadogInterceptor appears to read/await the entire response body before
returning, which never completes for an unbounded SSE stream — OkHttp Call.execute()
never resumes, so the SSE consumer (Apollo Kotlin's SseNetworkTransport in our case)
never starts parsing events.
3.8.0 with the same OkHttp interceptor stack works correctly: Call.execute()
returns on response headers and the streaming body is read incrementally.
Affected versions
- Broken:
com.datadoghq:dd-sdk-android-okhttp:3.9.0
- Working:
com.datadoghq:dd-sdk-android-okhttp:3.8.0
Environment
- Android
compileSdk 35, minSdk 26
- Kotlin 2.x
- OkHttp (transitive via Datadog/Apollo, default version)
- Apollo Kotlin 4.4.3 with a custom
SseNetworkTransport that wraps OkHttp via
Apollo's DefaultHttpEngine (uses synchronous call.execute() per
dd-sdk-android-gradle-plugin users will recognize)
Reproduction
- Build an
OkHttpClient with DatadogInterceptor registered as an
application interceptor:
OkHttpClient.Builder()
.addInterceptor(datadogInterceptor)
.readTimeout(0, TimeUnit.MILLISECONDS) // long-lived stream
.build()
Content-Type: text/event-stream and keeps the connection open
indefinitely (e.g. a graphql-sse subscription endpoint).
3. The server returns 200 OK and starts pushing
event: next\ndata: {...}\n\n chunks at ~1 Hz.
4. Observed on 3.9.0: OkHttp's Call.execute() does not return; the
client never reads any response body bytes. The connection sits idle
from the consumer's perspective even as the server is actively pushing.
5. Observed on 3.8.0: Call.execute() returns immediately on response
headers. Each pushed event is consumed and parsed.
What we ruled out
- Apollo / our own SSE transport: identical between 3.8.0 and 3.9.0 runs.
- Server: identical responses verified via a second client (web).
- OkHttp version: unchanged across the bump.
- Other interceptors in the stack (auth, tracing network interceptor, app
version header) — unchanged across the bump.
Workaround we applied
Strip DatadogInterceptor from the OkHttpClient used for SSE only:
authenticatedOkHttpClient.newBuilder()
.readTimeout(0, TimeUnit.MILLISECONDS)
.also { builder ->
builder.interceptors().removeAll { it is DatadogInterceptor }
}
.build()
This loses the per-request DatadogInterceptor reporting (RUM resource
tracking) for the streaming request, which would be inaccurate for SSE
anyway. The network-layer TracingInterceptor and DatadogEventListener
are kept and continue to report APM spans / connection events.
Reproduction steps
Test SSE events real time subscription.
Logcat logs
No response
Expected behavior
SSE events do not break with using datadog interceptor
Affected SDK versions
3.8.0
Latest working SDK version
3.8.0
Did you confirm if the latest SDK version fixes the bug?
No
Kotlin / Java version
No response
Gradle / AGP version
No response
Other dependencies versions
No response
Device Information
No response
Other relevant information
No response
Describe the bug
Summary
Upgrading
com.datadoghq:dd-sdk-android-okhttpfrom 3.8.0 → 3.9.0 breaksGraphQL subscriptions delivered over Server-Sent Events (SSE). On 3.9.0, the
DatadogInterceptorappears to read/await the entire response body beforereturning, which never completes for an unbounded SSE stream —
OkHttp Call.execute()never resumes, so the SSE consumer (Apollo Kotlin's
SseNetworkTransportin our case)never starts parsing events.
3.8.0 with the same OkHttp interceptor stack works correctly:
Call.execute()returns on response headers and the streaming body is read incrementally.
Affected versions
com.datadoghq:dd-sdk-android-okhttp:3.9.0com.datadoghq:dd-sdk-android-okhttp:3.8.0Environment
compileSdk35,minSdk26SseNetworkTransportthat wraps OkHttp viaApollo's
DefaultHttpEngine(uses synchronouscall.execute()perdd-sdk-android-gradle-pluginusers will recognize)Reproduction
OkHttpClientwithDatadogInterceptorregistered as anapplication interceptor:
Content-Type: text/event-stream and keeps the connection open
indefinitely (e.g. a graphql-sse subscription endpoint).
3. The server returns 200 OK and starts pushing
event: next\ndata: {...}\n\n chunks at ~1 Hz.
4. Observed on 3.9.0: OkHttp's Call.execute() does not return; the
client never reads any response body bytes. The connection sits idle
from the consumer's perspective even as the server is actively pushing.
5. Observed on 3.8.0: Call.execute() returns immediately on response
headers. Each pushed event is consumed and parsed.
What we ruled out
version header) — unchanged across the bump.
Workaround we applied
Strip DatadogInterceptor from the OkHttpClient used for SSE only:
authenticatedOkHttpClient.newBuilder()
.readTimeout(0, TimeUnit.MILLISECONDS)
.also { builder ->
builder.interceptors().removeAll { it is DatadogInterceptor }
}
.build()
This loses the per-request DatadogInterceptor reporting (RUM resource
tracking) for the streaming request, which would be inaccurate for SSE
anyway. The network-layer TracingInterceptor and DatadogEventListener
are kept and continue to report APM spans / connection events.
Reproduction steps
Test SSE events real time subscription.
Logcat logs
No response
Expected behavior
SSE events do not break with using datadog interceptor
Affected SDK versions
3.8.0
Latest working SDK version
3.8.0
Did you confirm if the latest SDK version fixes the bug?
No
Kotlin / Java version
No response
Gradle / AGP version
No response
Other dependencies versions
No response
Device Information
No response
Other relevant information
No response