raise ClickException when OAuthRefreshException occurs during watch command#2480
raise ClickException when OAuthRefreshException occurs during watch command#2480simonc56 wants to merge 7 commits into
Conversation
af7ab7e to
b2c0bad
Compare
|
046fbef commit message doesn't match the changes in it. the commit should be split to two based on their changes. |
|
idk. this sounds, so wrong. background task has channels, so send your messages to it, if the problem you're solving is from two different processes. But I don't have capacity right now to do this myself. |
There was a problem hiding this comment.
Pull request overview
This PR aims to make the watch command terminate when Trakt token refresh fails (OAuthRefreshException), instead of continuing to run and repeatedly failing scrobbles until manually restarted. It introduces a shared “fatal error” state used to propagate OAuth refresh failures from background/event-dispatch contexts back to the main watch loop, and converts the failure into a CLI-facing error.
Changes:
- Add a thread-safe
FatalErrorStateand plumb it through the watch WebSocket listener, event dispatcher, and background task runner. - Record
OAuthRefreshExceptionin background/event contexts and re-surface it in the main watch loop (then wrap asClickException). - Add tests and a test helper to validate fatal-error propagation behavior.
Reviewed changes
Copilot reviewed 10 out of 10 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| tests/test_watch_fatal_error.py | New tests covering fatal error propagation from background task and websocket listener. |
| tests/test_events.py | Adds coverage for dispatcher behavior when an OAuth refresh exception occurs. |
| tests/conftest.py | Adds a helper to construct an OAuthRefreshException for tests. |
| plextraktsync/watch/WebSocketListener.py | Adds fatal error checks and passes fatal state into the dispatcher. |
| plextraktsync/watch/FatalErrorState.py | Introduces a shared, thread-safe fatal error container. |
| plextraktsync/watch/EventDispatcher.py | Records OAuthRefreshException into fatal state and re-raises it. |
| plextraktsync/queue/BackgroundTask.py | Records OAuthRefreshException into fatal state and checks/raises fatal state during processing. |
| plextraktsync/media/MediaFactory.py | Ensures OAuthRefreshException is not swallowed by generic Trakt error handling. |
| plextraktsync/factory/Factory.py | Adds watch_fatal_error and wires it into websocket listener and background queue. |
| plextraktsync/commands/watch.py | Catches OAuthRefreshException from watch loop and raises a ClickException. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| except OAuthRefreshException as e: | ||
| if self.fatal_error is not None: | ||
| self.fatal_error.set(e) | ||
| return | ||
| raise |
|
This seems to solve the symptom, but the implementation feels broader than the requirement. The issue appears to need watch mode to terminate on |
Merge readinessnot obviously wrong, but likely over-engineered.
|
|
i'l give copilot a try to make it more simple |
I agree the |
|
It looks complicated to me, here is the plan from GPT-5.4 when asked to simplify the PR and try to remove the fatal error state logic to keep a simple re-raise logic if possible : Plan: Simplify Watch OAuth Failure PathThe PR can be simplified, but not all the way down to plain re-raise everywhere. The key finding is that there really are thread boundaries here: PlexAPI alert callbacks are started through an AlertListener, and the Trakt queue work runs on a daemon thread. That means a full removal of the fatal-error bridge would drop real failures on the floor. The narrower simplification is to keep one shared bridge for off-thread failures, but remove the extra propagation logic from the generic event dispatcher. Plan
ScopeIncluded:
Excluded:
Verification
|
…handling and streamline OAuthRefreshException
|
pytrakt has been updated to 4.3.0: |
Catch trakt
OAuthRefreshExceptionduring the watch command and terminates the process with Critical error.Before this PR, the script would continue trying to scrobble with invalid oauth token and user had to check logs to see if auth error happened and manually restart script or container. Now, it terminates automatically because this error is Critical and docker container can be restarted by restart policy.
To be sure we raise the error, a watch
FatalErrorStateis shared in theEventDispatcherand theBackgroundTask. The scrobble actions are executed in background with the dispatcher so when they raise an OAuthRefreshException it could not be catched normally inwatch.py. Hence the centralized fatal error state. Maybe it's overkill, any better suggestion is welcome.I was helped by GPT-5.4 AI to code this feature (but not to write this PR message 😉).
I tested it and the main objective is reached : PlexTraktSync terminates with CRITICAL error when refresh token fails during
watchcommand running.This PR needs this pytrakt PR merged too :
PS: When the python process stops with
ClickException, the docker container sees the main process finished with code 1. Thus, container manager can restart the container if needed and eventually a new valid oauth token can be read from.pytrakt.jsonfile (if updated by another PTS instance).fixes #2323