Skip to content

fix(givtcp): treat a 200 REST response that is not a JSON object as a failed read (#2828) - #5619

Merged
springfall2008 merged 1 commit into
mainfrom
fix/givtcp-rest-non-json-2828
Oct 19, 2026
Merged

springfall2008 merged 1 commit into
mainfrom
fix/givtcp-rest-non-json-2828

Conversation

@chalfontchubby

Copy link
Copy Markdown
Collaborator

Posted by Claude on behalf of @chalfontchubby.

Fixes #2828.

Summary

GivTCP can answer the readData status request with a 200 and an empty body while it is starting up, and a proxy in front of it can answer with an error page or a bare value. GivTCPRest.get_data() called r.json() unguarded. The decode error (or, for a bare number, a TypeError from read_data()'s "Control" in json check) escaped GivTCPComponent.run() and cut that poll short. Later inverters went unread and nothing was published for the pass.

get_data() now returns None for any 200 whose body is not a JSON object, which is what it already returns for a non-200. read_data() then retries it and gives up through its existing path. The warning includes the first 100 characters of the body, so a booting GivTCP (empty body) can be told apart from a misconfigured URL or proxy (HTML).

Behaviour change

A non-JSON 200 now goes through read_data()'s retry ladder (20s, then 40s sleeps) instead of failing the component pass at once. That is the same treatment a 503 already gets, and the ladder exists for exactly this case: a GivTCP that is still booting.

Tests

  • New test_get_data_rejects_a_200_that_is_not_json in test_givtcp_rest.py covers an empty body, an HTML error page and a bare 503. It fails on main (raises, or returns 503) and passes with the fix.
  • ./run_all --quick: all tests pass. run_pre_commit: all hooks pass.

Review

  • A cold subagent review found nothing.
  • A local /code-review high raised 8 findings. Three are fixed here: the code comment and commit message gave the wrong rationale, valid JSON that is not an object still crashed, and the warning didn't log the body.
  • Left alone deliberately:
    • The retry ladder's blocking (see Behaviour change above).
    • The existing trailing 40s sleep after the last retry, which is a separate change.
    • A test of read_data()'s retries, since test_read_data_retries_a_bad_response_then_gives_up already covers it.
    • Two style-only items.

🤖 Generated with Claude Code

… failed read (#2828)

GivTCP can answer readData with a 200 and an empty body while it is
starting up, and a proxy in front of it can answer with an error page or
a bare value. get_data() called r.json() unguarded, so the decode error
(or, for a bare number, a TypeError from read_data()'s "Control" check)
escaped GivTCPComponent.run() and cut that poll short, leaving later
inverters unread and nothing published for the pass.

get_data() now returns None for anything that is not a JSON object, the
same as a non-200, so read_data() retries it and then gives up as it
already does for any other failed GET. The warning includes the first
100 characters of the body so a booting GivTCP can be told apart from a
misconfigured URL.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
@github-actions github-actions Bot added the BOT_REVIEW Trigger an autotriage label Oct 18, 2026

@springfall2008 springfall2008 left a comment

Copy link
Copy Markdown
Owner

Choose a reason for hiding this comment

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

Automated comment from the triage bot.

Automated review of head 2397510: 0 findings inline, 0 in this body — the change looks sound.

Verified: get_data()'s only production caller is read_data() (givtcp_rest.py:472), which handles None through its existing retry ladder and now always receives a dict or None — so both the empty-body JSONDecodeError from #2828 and the bare-number "Control" in json TypeError are contained where they previously escaped GivTCPComponent.run(); the component's poll (givtcp.py:491) and rediscover() (givtcp.py:577) only use the read result when truthy. The old #2828 traceback's rest_getData in inverter.py is the pre-#4864 layout; the fix location on current main is correct. The new test fails on main for all three bodies (raises on the empty and HTML bodies, returns the bare 503 under return r.json()), exercises every branch of the fix including the log line, is registered in run_givtcp_rest_tests (test_givtcp_rest.py:717), and restores givtcp_rest.requests in a finally. No settings, entities or defaults change, so no docs update is needed.

@springfall2008 springfall2008 removed the BOT_REVIEW Trigger an autotriage label Oct 18, 2026
@springfall2008
springfall2008 merged commit 697c44c into main Oct 19, 2026
3 checks passed
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.

Problems at predbat startup - JSON error?

2 participants