Describe the bug
The sw_lastWatched getter (rule field "Newest episode view date") returns the last-view date by reading watchHistory[0].viewedAt, guarded only by a truthy check on watchHistory. Because watchHistory is an array, an empty result ([]) is truthy, so for any show/season with no play history it takes the date branch and reads viewedAt off undefined, throwing TypeError: Cannot read properties of undefined (reading 'viewedAt').
The error is caught by the getter (logged as Plex-Getter - Action failed …), so it isn't fatal — but it has a real downstream effect (see Observed impact below).
To Reproduce
- Have a Plex library with at least one show that has no play history (never watched).
- Create a TV rule using "Newest episode view date" (e.g. is before — 180 days ago).
- Run the rule with debug logging enabled.
- A
TypeError … reading 'viewedAt' is logged for every never-watched show evaluated.
Expected behavior
A show with no view history has no "newest episode view date", so the getter should return null (as its own : null fallback already intends) and the condition simply doesn't match — no exception, no caught error per item.
Device
- OS: Linux (Docker)
- Version: present on
development @ v3.15.0 (confirmed by code inspection); trace below is from a deployment tracking development
- Server: Plex
Debug logs
[DEBUG] [PlexGetterService] Plex-Getter - Action failed for '24' with id '3559': Cannot read properties of undefined (reading 'viewedAt')
TypeError: Cannot read properties of undefined (reading 'viewedAt')
at PlexGetterService.get (.../rules/getter/plex-getter.service.js // sw_lastWatched case)
at async .../rules/helpers/rule.comparator.service.js:166:32
at async Promise.all (index 3)
at async RuleComparatorService.executeRule (.../rule.comparator.service.js:163:13)
at async RuleComparatorService.executeRulesWithData (.../rule.comparator.service.js:102:21)
at async RuleExecutorService.executeForRuleGroups (.../tasks/rule-executor.service.js)
Additional context
Root cause — apps/server/src/modules/rules/getter/plex-getter.service.ts, sw_lastWatched case (current development):
return watchHistory
? new Date(+watchHistory[0].viewedAt * 1000)
: null;
For a never-watched show watchHistory is []; [] is truthy, so watchHistory[0] is undefined. A length guard (watchHistory?.length ? …) returns null as the existing : null branch already intends.
Observed impact (downstream of the throw)
The caught exception surfaces from the getter as undefined. The rule comparator treats a getter that yields undefined as a transient failure (intended for transport errors), and the executor preserves transient-failed items in their collection rather than removing them. The net effect: shows with no view history are never evaluated to a definitive result against a sw_lastWatched condition, so they can get stuck in a collection across runs even when they no longer match the rule.
Concrete example from a ~1,400-show Plex library: a "leaving soon" style rule had 152 never-watched shows sitting in its collection that no longer matched the rule. All 152 were confirmed to have zero history via Plex's own /status/sessions/history/all?metadataItemID=<id> endpoint. They persisted run after run purely because this getter threw (→ transient → preserved). With the getter returning null instead of throwing, the same run evaluated them honestly and the collection dropped to 0 — i.e. they had been phantom members the whole time. (Flagging since this is Plex-specific and may be hard to reproduce without a Plex server.)
Describe the bug
The
sw_lastWatchedgetter (rule field "Newest episode view date") returns the last-view date by readingwatchHistory[0].viewedAt, guarded only by a truthy check onwatchHistory. BecausewatchHistoryis an array, an empty result ([]) is truthy, so for any show/season with no play history it takes the date branch and readsviewedAtoffundefined, throwingTypeError: Cannot read properties of undefined (reading 'viewedAt').The error is caught by the getter (logged as
Plex-Getter - Action failed …), so it isn't fatal — but it has a real downstream effect (see Observed impact below).To Reproduce
TypeError … reading 'viewedAt'is logged for every never-watched show evaluated.Expected behavior
A show with no view history has no "newest episode view date", so the getter should return
null(as its own: nullfallback already intends) and the condition simply doesn't match — no exception, no caught error per item.Device
development@ v3.15.0 (confirmed by code inspection); trace below is from a deployment trackingdevelopmentDebug logs
Additional context
Root cause —
apps/server/src/modules/rules/getter/plex-getter.service.ts,sw_lastWatchedcase (currentdevelopment):For a never-watched show
watchHistoryis[];[]is truthy, sowatchHistory[0]isundefined. A length guard (watchHistory?.length ? …) returnsnullas the existing: nullbranch already intends.Observed impact (downstream of the throw)
The caught exception surfaces from the getter as
undefined. The rule comparator treats a getter that yieldsundefinedas a transient failure (intended for transport errors), and the executor preserves transient-failed items in their collection rather than removing them. The net effect: shows with no view history are never evaluated to a definitive result against asw_lastWatchedcondition, so they can get stuck in a collection across runs even when they no longer match the rule.Concrete example from a ~1,400-show Plex library: a "leaving soon" style rule had 152 never-watched shows sitting in its collection that no longer matched the rule. All 152 were confirmed to have zero history via Plex's own
/status/sessions/history/all?metadataItemID=<id>endpoint. They persisted run after run purely because this getter threw (→ transient → preserved). With the getter returningnullinstead of throwing, the same run evaluated them honestly and the collection dropped to 0 — i.e. they had been phantom members the whole time. (Flagging since this is Plex-specific and may be hard to reproduce without a Plex server.)