Skip to content

Handle non-UTF-8 and invalid JSON responses from WLED device#2052

Merged
frenck merged 3 commits into
frenck:mainfrom
mik-laj:fix/handle-invalid-response
May 8, 2026
Merged

Handle non-UTF-8 and invalid JSON responses from WLED device#2052
frenck merged 3 commits into
frenck:mainfrom
mik-laj:fix/handle-invalid-response

Conversation

@mik-laj
Copy link
Copy Markdown
Collaborator

@mik-laj mik-laj commented May 4, 2026

Proposed Changes

Some WLED devices can return corrupt data that causes an unhandled exception in the integration, silently presenting the user with a generic "Failed to connect" or "An unknown error occurred" message with no diagnostic information in the logs.

Two concrete failure modes observed in the wild ( home-assistant/core#162199
):

  1. Corrupt presets.json — the device returns binary garbage (e.g. bytes starting with \xff\xfe), which causes response.text() to raise UnicodeDecodeError before any JSON parsing takes place.
  2. Invalid JSON — the device returns a response with Content-Type: application/json but the body is not valid JSON, causing orjson.loads() to raise JSONDecodeError.

Both exceptions were previously unhandled and propagated as unexpected errors. This change catches them explicitly and raises WLEDInvalidResponseError (a new exception class) with a message that includes the HTTP method and URI, making it straightforward to identify the problematic endpoint in logs.

Changes:

  • Add WLEDInvalidResponseError(WLEDError) to exceptions.py
  • Wrap response.text() in a try/except UnicodeDecodeError
  • Wrap orjson.loads() in a try/except orjson.JSONDecodeError
  • Add tests for all four new error paths (/json and /presets.json × non-UTF-8 and invalid JSON)

References: home-assistant/core#162199

Related Issues

@coderabbitai
Copy link
Copy Markdown

coderabbitai Bot commented May 4, 2026

Warning

Rate limit exceeded

@mik-laj has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 31 minutes and 59 seconds before requesting another review.

You’ve run out of usage credits. Purchase more in the billing tab.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 8762756e-da03-41be-9bbf-b6155b034938

📥 Commits

Reviewing files that changed from the base of the PR and between 2c2bb40 and 02a12ad.

📒 Files selected for processing (4)
  • src/wled/__init__.py
  • src/wled/exceptions.py
  • src/wled/wled.py
  • tests/test_wled.py
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Tip

💬 Introducing Slack Agent: The best way for teams to turn conversations into code.

Slack Agent is built on CodeRabbit's deep understanding of your code, so your team can collaborate across the entire SDLC without losing context.

  • Generate code and open pull requests
  • Plan features and break down work
  • Investigate incidents and troubleshoot customer tickets together
  • Automate recurring tasks and respond to alerts with triggers
  • Summarize progress and report instantly

Built for teams:

  • Shared memory across your entire org—no repeating context
  • Per-thread sandboxes to safely plan and execute work
  • Governance built-in—scoped access, auditability, and budget controls

One agent for your entire SDLC. Right inside Slack.

👉 Get started


Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

@mik-laj mik-laj added the new-feature New features or options. label May 4, 2026
@codecov
Copy link
Copy Markdown

codecov Bot commented May 4, 2026

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 97.25%. Comparing base (3e87d76) to head (02a12ad).
⚠️ Report is 598 commits behind head on main.

Additional details and impacted files
@@             Coverage Diff             @@
##             main    #2052       +/-   ##
===========================================
+ Coverage   58.61%   97.25%   +38.64%     
===========================================
  Files           6        8        +2     
  Lines         662     1094      +432     
  Branches      143      112       -31     
===========================================
+ Hits          388     1064      +676     
+ Misses        270       20      -250     
- Partials        4       10        +6     

☔ View full report in Codecov by Sentry.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

mik-laj and others added 2 commits May 8, 2026 00:42
Raises WLEDInvalidResponseError instead of crashing with an unhandled
UnicodeDecodeError or JSONDecodeError when the device returns corrupt
data (e.g. binary garbage in presets.json).

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
@mik-laj mik-laj force-pushed the fix/handle-invalid-response branch from 81a5bbd to 142065c Compare May 7, 2026 22:43
@mik-laj mik-laj marked this pull request as ready for review May 7, 2026 22:45
@mik-laj mik-laj requested review from Copilot and joostlek May 7, 2026 22:45
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

This PR improves robustness of the WLED client when devices return corrupt responses by converting non-UTF-8 and invalid-JSON payload failures into a dedicated, more diagnostic exception (WLEDInvalidResponseError) rather than letting decode/parse errors propagate as unexpected exceptions.

Changes:

  • Introduces WLEDInvalidResponseError to represent invalid/corrupt responses from the device.
  • Catches UnicodeDecodeError from response.text() and orjson.JSONDecodeError from orjson.loads() and raises WLEDInvalidResponseError including {method} {uri} context.
  • Adds update() tests covering corrupt /json and /presets.json responses (invalid JSON and non-UTF-8).

Reviewed changes

Copilot reviewed 3 out of 3 changed files in this pull request and generated 3 comments.

File Description
src/wled/wled.py Adds explicit handling for non-UTF-8 and invalid JSON responses in the request pipeline.
src/wled/exceptions.py Adds a new exception type WLEDInvalidResponseError.
tests/test_wled.py Adds parametrized tests for corrupt /json and /presets.json update flows.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/wled/wled.py Outdated
Comment thread src/wled/exceptions.py
Comment thread tests/test_wled.py
- Wrap orjson.loads in 4xx/5xx JSON branch with try/except JSONDecodeError
- Wrap non-UTF-8 decode in 4xx/5xx text branch to raise WLEDInvalidResponseError
  with method+URI instead of letting UnicodeDecodeError escape
- Use `"application/json" in content_type` in error branch (consistent with
  success path)
- Export WLEDInvalidResponseError from wled/__init__.py and __all__
- Assert exception message contains method+URI in corrupt-response tests
- Add parametrized test for unparsable HTTP error bodies (non-UTF-8 and
  invalid JSON)

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Copy link
Copy Markdown
Owner

@frenck frenck left a comment

Choose a reason for hiding this comment

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

Thanks, @mik-laj 👍

../Frenck

                       

Blogging my personal ramblings at frenck.dev

@frenck frenck merged commit e40e18a into frenck:main May 8, 2026
18 checks passed
@github-actions github-actions Bot locked and limited conversation to collaborators May 9, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

new-feature New features or options.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants