Android: fix HttpURLConnection leak in TalkModeVoiceResolver#43780
Android: fix HttpURLConnection leak in TalkModeVoiceResolver#43780obviyus merged 3 commits intoopenclaw:mainfrom
Conversation
Greptile SummaryThis PR fixes a real
Confidence Score: 3/5
Prompt To Fix All With AIThis is a comment left during a code review.
Path: apps/android/app/src/main/java/ai/openclaw/app/voice/TalkModeVoiceResolver.kt
Line: 89-90
Comment:
**Null `errorStream` causes NPE on error responses with no body**
`HttpURLConnection.getErrorStream()` returns `null` when the server returns a 4xx/5xx response with no body (per Android docs). On that path `stream` becomes `null`, and `stream.use { it.readBytes() }` will pass `null` as `it` and crash with a `NullPointerException` when `readBytes()` is called on it.
Use the safe-call operator and fall back to an empty array so the subsequent error message path still works cleanly:
```suggestion
val stream = if (code >= 400) conn.errorStream else conn.inputStream
val data = stream?.use { it.readBytes() } ?: byteArrayOf()
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: apps/android/app/src/main/java/ai/openclaw/app/voice/TalkModeVoiceResolver.kt
Line: 104-106
Comment:
**`conn.disconnect()` defeats HTTP keep-alive / connection reuse**
On Android 4.4+ `HttpURLConnection` is backed by OkHttp. Simply closing the response stream (already done by `stream.use {}` above) returns the underlying TCP connection to the pool for keep-alive reuse. Calling `conn.disconnect()` unconditionally in `finally` then immediately tears that connection down, which negates the pooling benefit and adds latency for every subsequent call to `listVoices()`.
The `finally` block is still useful for the case where an exception is thrown before `stream` is ever opened (e.g. a connection timeout), but in the happy path it is counterproductive. Consider disconnecting only on error, or removing it entirely and relying on stream-close for cleanup:
```kotlin
} catch (e: Exception) {
conn.disconnect()
throw e
}
```
How can I resolve this? If you propose a fix, please make it concise.Last reviewed commit: 63e946e |
| val stream = if (code >= 400) conn.errorStream else conn.inputStream | ||
| val data = stream.use { it.readBytes() } |
There was a problem hiding this comment.
Null errorStream causes NPE on error responses with no body
HttpURLConnection.getErrorStream() returns null when the server returns a 4xx/5xx response with no body (per Android docs). On that path stream becomes null, and stream.use { it.readBytes() } will pass null as it and crash with a NullPointerException when readBytes() is called on it.
Use the safe-call operator and fall back to an empty array so the subsequent error message path still works cleanly:
| val stream = if (code >= 400) conn.errorStream else conn.inputStream | |
| val data = stream.use { it.readBytes() } | |
| val stream = if (code >= 400) conn.errorStream else conn.inputStream | |
| val data = stream?.use { it.readBytes() } ?: byteArrayOf() |
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/android/app/src/main/java/ai/openclaw/app/voice/TalkModeVoiceResolver.kt
Line: 89-90
Comment:
**Null `errorStream` causes NPE on error responses with no body**
`HttpURLConnection.getErrorStream()` returns `null` when the server returns a 4xx/5xx response with no body (per Android docs). On that path `stream` becomes `null`, and `stream.use { it.readBytes() }` will pass `null` as `it` and crash with a `NullPointerException` when `readBytes()` is called on it.
Use the safe-call operator and fall back to an empty array so the subsequent error message path still works cleanly:
```suggestion
val stream = if (code >= 400) conn.errorStream else conn.inputStream
val data = stream?.use { it.readBytes() } ?: byteArrayOf()
```
How can I resolve this? If you propose a fix, please make it concise.| } finally { | ||
| conn.disconnect() | ||
| } |
There was a problem hiding this comment.
conn.disconnect() defeats HTTP keep-alive / connection reuse
On Android 4.4+ HttpURLConnection is backed by OkHttp. Simply closing the response stream (already done by stream.use {} above) returns the underlying TCP connection to the pool for keep-alive reuse. Calling conn.disconnect() unconditionally in finally then immediately tears that connection down, which negates the pooling benefit and adds latency for every subsequent call to listVoices().
The finally block is still useful for the case where an exception is thrown before stream is ever opened (e.g. a connection timeout), but in the happy path it is counterproductive. Consider disconnecting only on error, or removing it entirely and relying on stream-close for cleanup:
} catch (e: Exception) {
conn.disconnect()
throw e
}Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/android/app/src/main/java/ai/openclaw/app/voice/TalkModeVoiceResolver.kt
Line: 104-106
Comment:
**`conn.disconnect()` defeats HTTP keep-alive / connection reuse**
On Android 4.4+ `HttpURLConnection` is backed by OkHttp. Simply closing the response stream (already done by `stream.use {}` above) returns the underlying TCP connection to the pool for keep-alive reuse. Calling `conn.disconnect()` unconditionally in `finally` then immediately tears that connection down, which negates the pooling benefit and adds latency for every subsequent call to `listVoices()`.
The `finally` block is still useful for the case where an exception is thrown before `stream` is ever opened (e.g. a connection timeout), but in the happy path it is counterproductive. Consider disconnecting only on error, or removing it entirely and relying on stream-close for cleanup:
```kotlin
} catch (e: Exception) {
conn.disconnect()
throw e
}
```
How can I resolve this? If you propose a fix, please make it concise.|
@greptile-apps Addressed both review comments:
|
ef7f7d2 to
ef341de
Compare
* main: (168 commits) fix: stabilize macos daemon onboarding fix(ui): keep shared auth on insecure control-ui connects (openclaw#45088) docs(plugins): clarify workspace shadowing fix(node-host): harden perl approval binding fix(node-host): harden pnpm approval binding fix(discovery): add missing domain to wideArea Zod config schema (openclaw#35615) chore(gitignore): add docker-compose override (openclaw#42879) feat(ios): add onboarding welcome pager (openclaw#45054) fix(signal): add groups config to Signal channel schema (openclaw#27199) fix: restore web fetch firecrawl config in runtime zod schema (openclaw#42583) fix: polish Android QR scanner onboarding (openclaw#45021) fix(android): use Google Code Scanner for onboarding QR fix(config): add missing params field to agents.list[] validation schema (openclaw#41171) docs(contributing): update Android app ownership fix(agents): rephrase session reset prompt to avoid Azure content filter (openclaw#43403) test(config): cover requiresOpenAiAnthropicToolPayload in compat schema fixture fix(agents): respect explicit user compat overrides for non-native openai-completions (openclaw#44432) Android: fix HttpURLConnection leak in TalkModeVoiceResolver (openclaw#43780) Docker: add OPENCLAW_TZ timezone support (openclaw#34119) fix(agents): avoid injecting memory file twice on case-insensitive mounts (openclaw#26054) ...
…w#43780) * Android: fix HttpURLConnection leak in TalkModeVoiceResolver.listVoices * fix null errorStream NPE and preserve HTTP keep-alive * fix: restore voice resolver disconnect cleanup --------- Co-authored-by: Ayaan Zaidi <[email protected]>
…w#43780) * Android: fix HttpURLConnection leak in TalkModeVoiceResolver.listVoices * fix null errorStream NPE and preserve HTTP keep-alive * fix: restore voice resolver disconnect cleanup --------- Co-authored-by: Ayaan Zaidi <[email protected]>
…w#43780) * Android: fix HttpURLConnection leak in TalkModeVoiceResolver.listVoices * fix null errorStream NPE and preserve HTTP keep-alive * fix: restore voice resolver disconnect cleanup --------- Co-authored-by: Ayaan Zaidi <[email protected]> (cherry picked from commit 84428bb)
…w#43780) * Android: fix HttpURLConnection leak in TalkModeVoiceResolver.listVoices * fix null errorStream NPE and preserve HTTP keep-alive * fix: restore voice resolver disconnect cleanup --------- Co-authored-by: Ayaan Zaidi <[email protected]>
…w#43780) * Android: fix HttpURLConnection leak in TalkModeVoiceResolver.listVoices * fix null errorStream NPE and preserve HTTP keep-alive * fix: restore voice resolver disconnect cleanup --------- Co-authored-by: Ayaan Zaidi <[email protected]> (cherry picked from commit 84428bb)
…w#43780) * Android: fix HttpURLConnection leak in TalkModeVoiceResolver.listVoices * fix null errorStream NPE and preserve HTTP keep-alive * fix: restore voice resolver disconnect cleanup --------- Co-authored-by: Ayaan Zaidi <[email protected]> (cherry picked from commit 84428bb)
Summary
TalkModeVoiceResolver.listVoices()opens anHttpURLConnectionto the ElevenLabs API but never callsconn.disconnect(), and the responseInputStreamis never closed. Each call leaks a connection, which accumulates over repeated voice list fetches and can exhaust the connection pool.Fix
try/finally { conn.disconnect() }to ensure the connection is released on all paths (success, HTTP error, parse error, exception).stream.use { it.readBytes() }to close the InputStream after reading.Test plan
conn.disconnect()runs on all exit paths (normal return, HTTP 4xx/5xx throw, JSON parse exception).stream.use {}closes bothinputStreamanderrorStreampaths.@steipete @obviyus Would appreciate a review when you have a chance.
Made with Cursor