Skip to content

fix: Bound authentication and listener recovery retries - #2262

Merged
iMicknl merged 1 commit into
mainfrom
codex/bounded-auth-recovery
Sep 27, 2026
Merged

iMicknl merged 1 commit into
mainfrom
codex/bounded-auth-recovery

Conversation

@iMicknl

@iMicknl iMicknl commented Sep 27, 2026 •

Copy link
Copy Markdown
Owner

Summary

Makes session and event-listener recovery bounded and non-redundant:

  • Relogin no longer registers a listener. relogin now calls login(register_event_listener=False). Before, recovering from NotAuthenticatedError created a listener that the retried call (e.g. register_event_listener) immediately replaced.
  • Auth gets its own connection-retry budget. The new _authenticate() wraps self._auth.login() in retry_on_connection_failure and clears the listener ID, so a registration outage never restarts a completed login.
  • Local token validation no longer recurses. It now calls _get("setup/gateways") instead of get_gateways(), so a rejected token fails once instead of going back through retry_on_auth_error → relogin.
  • register_event_listener recovers from expired sessions (@retry_on_auth_error) and clears the listener ID before posting, because the server may have replaced the old listener even if the response was lost.
  • fetch_events registers a listener on demand when none is active (after relogin, or after a failed registration). A later poll can then recover.
  • docs/resiliency.md now documents the recovery semantics.

Behavior changes

No public signatures change. Two observable differences:

  • login() now clears event_listener_id, including when register_event_listener=False.
  • fetch_events() without a registered listener now registers one instead of failing on events/None/fetch.
  • A local login(register_event_listener=False) no longer fills client.gateways. It still validates the token, but through _get so it cannot recurse into re-login. Nothing in Home Assistant reads this attribute.

Tests

tests/test_client_recovery.py adds 12 regression tests: no double registration, bounded auth and transport budgets, errors preserved after exhaustion, BadCredentialsError / InvalidURL not retried, local validation without recursion, and full OAuth → register → fetch recovery without auth mocks.

Supersedes #2233.

Automatic reauthentication no longer registers an event listener that the
retried request immediately replaces, and local token validation no longer
recurses through the auth retry decorator. Authentication gets its own
connection-retry budget, listener registration recovers from expired
sessions, and fetch_events registers a replacement listener when none is
active.
@github-actions github-actions Bot added the bug Something isn't working label Sep 27, 2026
@iMicknl
iMicknl marked this pull request as ready for review September 27, 2026 17:23
@iMicknl
iMicknl requested a review from tetienne as a code owner September 27, 2026 17:23
Copilot AI balanced review requested due to automatic review settings September 27, 2026 17:23

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

Copilot review overview

🟡 Changes recommended

On-demand registration nests authentication retry budgets, exceeding the documented attempt limit.

Review effort: Balanced
Findings: 1 Medium severity

Open (1)
What changed in this PR

Bounds authentication and event-listener recovery retries.

Changes:

  • Separates authentication retries from listener registration.
  • Adds on-demand listener recovery and regression tests.
  • Documents recovery behavior.
File Description
pyoverkiz/​client.py Refactors authentication and listener recovery.
tests/​test_client.py Updates fetch-event test setup.
tests/​test_client_recovery.py Adds recovery regression coverage.
docs/​resiliency.md Documents retry semantics.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread pyoverkiz/client.py
Comment on lines +500 to +501
if self.event_listener_id is None:
await self.register_event_listener()

Copy link
Copy Markdown
Owner Author

Choose a reason for hiding this comment

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

You're right, but this only happens if the server rejects a new login straight away. Even then, it stops after a few tries. Not worth the extra code, so I'll leave it as is.

@iMicknl
iMicknl merged commit 1c37470 into main Sep 27, 2026
16 checks passed
@iMicknl
iMicknl deleted the codex/bounded-auth-recovery branch September 27, 2026 17:39
@dansrogers

Copy link
Copy Markdown

Fantastic thank you. Just wanted my home to stop crashing!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

bug Something isn't working

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants