Skip to content

fix(xtream): validate HTTP response before JSON parsing (Closes #338) - #437

Open
louzt wants to merge 1 commit into
Fredolx:mainfrom
louzt:fix/xtream-response-error-handling
Open

louzt wants to merge 1 commit into
Fredolx:mainfrom
louzt:fix/xtream-response-error-handling

Conversation

@louzt

@louzt louzt commented Jun 15, 2026

Copy link
Copy Markdown

Summary

Fix the expected value at line 1 column 1 error when Xtream API returns non-JSON responses (empty body, HTML error pages, HTTP 502/404). Two call sites fixed.

What Changed

Before: client.get(url).send().await?.json::<T>().await?

After: validates HTTP status + empty body + non-JSON content before deserializing.

Two functions patched: get_xtream_http_data<T>() and get_status().

Why / Why This Shape

  • Issue error decoding response body #338 root cause: Xtream API server returns HTML error (502/404) or empty response leading to .json() panic.
  • The fix gives a meaningful error message with HTTP status and body preview for debugging.

Compatibility Note

Valid Xtream API responses are unchanged.

Scope Boundary

  • Does NOT add retry logic.
  • Does NOT change the success path.
  • Does NOT add logging infrastructure.

Validation

cd src-tauri && cargo test
# → 2 passed; 0 failed

@CLAassistant

CLAassistant commented Jun 15, 2026

Copy link
Copy Markdown

CLA assistant check
All committers have signed the CLA.

@louzt
louzt marked this pull request as ready for review June 15, 2026 06:28
@louzt

louzt commented Jun 15, 2026

Copy link
Copy Markdown
Author

Operational Friction & Priority Context

To provide full transparency on (my) priority: this patch is my primary merge wish right now, as it resolves a critical operational block. I hit this constraint firsthand while troubleshooting my IPTV provider setup after they migrated their upstream endpoint URL and port.

Initially, I suspected an asymmetric routing mismatch between my local interface output and the active VPN layer. However, after reverse-engineering the endpoint exchanges and performing a deep dive into the local runtime caches and SQLite database schema, the root cause became explicit:

  • The Desync: Even when the root provider URL is updated cleanly via the visual UI onboarding component, historical channel rows remain tightly coupled to the legacy configuration string in the SQL tables.
  • The Deadlock: When the background worker triggers the data refresh pipeline, it attempts to poll the dead legacy port. The provider drops the transport or returns an HTML error block. Because the current reqwest pipeline tries to decode this non-JSON slop directly via .json(), it triggers a hard parsing panic.
  • The Failure State: This deadlock aborts the entire execution queue. Even though the UI says the config is updated, the internal refresh is completely blocked, leaving the local channels permanently frozen until manual database surgery or hard cache purging is performed.

State Machine Recovery Sequence:

sequenceDiagram
    autonumber
    participant UI as Visual Configuration UI
    participant DB as SQLite Local Database
    participant Worker as Background Refresh Task
    participant Prov as Upstream Provider (Mutated Port)

    UI->>DB: Updates base Source URL cleanly (Visual OK)
    Worker->>DB: Fetches historical channel rows (Stale Endpoint Refs)
    Worker->>Prov: Dispatches refresh request to legacy port
    Prov-->>Worker: Connection dropped or HTML text returned
    Note over Worker: reqwest drains non-JSON slop
    Worker->>Worker: Deserialization panic (Crashes queue)
    Note over DB: Channels remain permanently frozen
Loading

@louzt

louzt commented Jun 15, 2026

Copy link
Copy Markdown
Author

Would love to get this landed first to unblock standard data refresh pipelines, or allow me further research.

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.

2 participants