Skip to content

Commit d7a9870

Browse files
committed
Fix incomplete JSON response detection (not just empty)
- Added check for suspiciously short responses (< 10 chars) that indicate truncation - Updated error messages to show actual response content for debugging - Added test for incomplete JSON response (e.g., just ')') - Updated release notes to accurately describe the issue The actual problem was truncated/incomplete JSON responses (like ')'), not empty responses. This happens when hubs crash or restart mid-response.
1 parent 78dbb9a commit d7a9870

File tree

3 files changed

+58
-6
lines changed

3 files changed

+58
-6
lines changed

RELEASE_NOTES_3.3.md

Lines changed: 5 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -3,17 +3,17 @@
33
## Bug Fix: Hub Update Status Monitoring
44

55
### Problem
6-
During hub update monitoring with polling, the system would fail with "Cannot read Json element because of unexpected end of the input" errors when the hub returned empty responses. This typically happens when:
6+
During hub update monitoring with polling, the system would fail with "Cannot read Json element because of unexpected end of the input" errors when the hub returned incomplete/truncated JSON responses (e.g., just ")"). This typically happens when:
77
- The hub is busy processing an update
8-
- The hub is temporarily restarting
9-
- Network issues cause incomplete responses
8+
- The hub is temporarily restarting or crashing mid-response
9+
- Network issues cause response truncation
1010

1111
### Solution
1212
Added robust error handling and retry logic:
1313

14-
1. **Empty Response Detection**: Check for blank responses before attempting JSON parsing
14+
1. **Incomplete Response Detection**: Check for empty or suspiciously short responses (< 10 chars) before attempting JSON parsing
1515
2. **Retry Logic with Exponential Backoff**: Automatically retry failed version checks up to 3 times with increasing delays (2s, 4s, 8s)
16-
3. **Better Error Messages**: Clear error messages indicating when empty responses are received
16+
3. **Better Error Messages**: Clear error messages indicating when incomplete/malformed responses are received, showing the actual response content
1717

1818
### Changes Made
1919
- Added empty response check in `HubOperations.getHubVersions()`

src/main/kotlin/HubOperations.kt

Lines changed: 9 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -71,14 +71,22 @@ object HubOperations {
7171
val endpoint = "http://${hubIp}/apps/api/${makerApiAppId}/devices/${hub.id}"
7272
val responseBody = networkClient.getBody(endpoint, mapOf("access_token" to makerApiToken))
7373

74-
// Check for empty response
74+
// Check for empty or very short response (likely incomplete)
7575
if (responseBody.isBlank()) {
7676
throw Exception(
7777
"Failed to get hub info for hub '${hub.label}' from endpoint '$endpoint': " +
7878
"Received empty response. This may happen if the hub is busy or restarting."
7979
)
8080
}
8181

82+
if (responseBody.length < 10) {
83+
throw Exception(
84+
"Failed to get hub info for hub '${hub.label}' from endpoint '$endpoint': " +
85+
"Received incomplete response (${responseBody.length} chars): '$responseBody'. " +
86+
"This may happen if the hub is busy, restarting, or experiencing network issues."
87+
)
88+
}
89+
8290
try {
8391
val json = Json.parseToJsonElement(responseBody).jsonObject
8492
val attributes = json["attributes"] as? JsonArray

src/test/kotlin/integration/RealHubitatApiTest.kt

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -283,6 +283,50 @@ class RealHubitatApiTest : FunSpec({
283283
}
284284
}
285285

286+
test("should handle incomplete JSON response") {
287+
// Test with incomplete JSON (just a closing paren)
288+
val mockEngine = MockEngine { request ->
289+
respond(
290+
content = ByteReadChannel(")"),
291+
status = HttpStatusCode.OK,
292+
headers = headersOf(HttpHeaders.ContentType, "application/json")
293+
)
294+
}
295+
296+
val client = HttpClient(mockEngine)
297+
val networkClient = KtorNetworkClient(client)
298+
299+
val hub = Device.Hub(
300+
id = 445,
301+
label = "Test Hub",
302+
ip = "192.168.1.100",
303+
managementToken = "test-token"
304+
)
305+
306+
try {
307+
val result = runCatching {
308+
HubOperations.getHubVersions(
309+
hub = hub,
310+
networkClient = networkClient,
311+
hubIp = "192.168.1.100",
312+
makerApiAppId = "398",
313+
makerApiToken = "test-token"
314+
)
315+
}
316+
317+
result.isFailure shouldBe true
318+
val error = result.exceptionOrNull()
319+
error shouldNotBe null
320+
error?.message shouldContain "incomplete response"
321+
error?.message shouldContain ")"
322+
323+
println("✅ Incomplete JSON handled gracefully: ${error?.message?.take(150)}")
324+
325+
} finally {
326+
client.close()
327+
}
328+
}
329+
286330
test("should handle empty response gracefully") {
287331
// Test with mocked empty response
288332
val mockEngine = MockEngine { request ->

0 commit comments

Comments
 (0)