feat: Replace 401 errors with actionable token recovery guidance#39
Conversation
Plex API 401 responses now throw a message naming the cmdlets users can run to recover (Update-PatServerToken, Get-PatStoredServer, -Token) instead of bubbling up the raw HTTP status. Detection happens centrally in Invoke-PatApi so every public cmdlet that hits the Plex API benefits. Also fixes a latent test that constructed a [WebException] with an invalid 4-arg overload — the constructor failure was masking the assertion. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
No actionable comments were generated in the recent review. 🎉 ℹ️ Recent review info⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Run ID: 📒 Files selected for processing (1)
✅ Files skipped from review due to trivial changes (1)
📝 WalkthroughWalkthroughDetects 401/Unauthorized responses in Invoke-PatApi and throws an authentication guidance message naming recovery commands; unit tests expanded to validate 401 detection, preservation of original error text, and that non-401 Unauthorized exceptions use the generic error path. Changes
Estimated code review effort🎯 2 (Simple) | ⏱️ ~12 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches🧪 Generate unit tests (beta)
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. Comment |
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
There was a problem hiding this comment.
Pull request overview
Updates the internal Plex API invocation wrapper to replace generic 401 Unauthorized failures with actionable token-recovery guidance, and adjusts unit tests to validate the new messaging and fix an invalid WebException construction.
Changes:
- Add centralized 401 detection in
Invoke-PatApiand throw a guidance-focused error message. - Update
Invoke-PatApiunit tests to assert the new guidance and correct the prior invalid WebException constructor usage.
Reviewed changes
Copilot reviewed 2 out of 2 changed files in this pull request and generated 1 comment.
| File | Description |
|---|---|
| PlexAutomationToolkit/Private/Invoke-PatApi.ps1 | Adds special-case handling for 401-ish errors to provide token recovery instructions. |
| tests/Unit/Private/Invoke-PatApi.tests.ps1 | Updates/extends tests to validate the new 401 guidance messaging paths. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
There was a problem hiding this comment.
🧹 Nitpick comments (2)
PlexAutomationToolkit/Private/Invoke-PatApi.ps1 (1)
137-144: Consider tightening the 401 detection and preserving original error context.Two small concerns with the new branch:
- Matching the bare word
Unauthorized(independent of401) can trigger the 401 guidance for unrelated errors whose message happens to contain that word (e.g., a 403 body or a custom Plex error string mentioning "Unauthorized access"). Pairing the two checks (require both 401-ish status and Unauthorized wording, or drop the loneUnauthorizedclause) would reduce false positives without harming the intended cases — both test fixtures satisfy\b401\balready.- The 401 path discards
$errorMessageentirely, so the original wrapped exception text (URL, inner-exception details, etc.) is lost from the user-visible error. Appending the captured detail keeps the actionable guidance front-and-center while retaining diagnostics for support/debug scenarios.♻️ Proposed adjustment
- # 401 indicates a missing, expired, or invalid token. Surface - # actionable guidance instead of the raw HTTP error so callers - # know which cmdlets to run to recover. - if ($errorMessage -match '\b401\b' -or $errorMessage -match 'Unauthorized') { - throw ("Plex API returned 401 Unauthorized. The authentication token is missing, expired, or invalid. " + - "To resolve: refresh the token with 'Update-PatServerToken' (use -Name to target a non-default server), " + - "list configured servers with 'Get-PatStoredServer', " + - "or pass an explicit -Token parameter to the cmdlet you are calling.") - } + # 401 indicates a missing, expired, or invalid token. Surface + # actionable guidance instead of the raw HTTP error so callers + # know which cmdlets to run to recover. + if ($errorMessage -match '\b401\b') { + throw ("Plex API returned 401 Unauthorized. The authentication token is missing, expired, or invalid. " + + "To resolve: refresh the token with 'Update-PatServerToken' (use -Name to target a non-default server), " + + "list configured servers with 'Get-PatStoredServer', " + + "or pass an explicit -Token parameter to the cmdlet you are calling. " + + "Original error: $errorMessage") + }If you keep the
Unauthorizedclause for safety, consider also asserting onUpdate-PatServerTokenin the second 401 test (currently it only asserts on the "missing, expired, or invalid" wording) so the cmdlet-naming contract is covered for both message shapes.🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@PlexAutomationToolkit/Private/Invoke-PatApi.ps1` around lines 137 - 144, The 401 branch in Invoke-PatApi is too broad and discards diagnostic text; update the conditional to only trigger when the status indicates 401 (e.g. $errorMessage -match '\b401\b') or when both a 401-like status and the word "Unauthorized" are present (avoid a lone 'Unauthorized' match), and modify the thrown message to append the original $errorMessage (preserve URL/inner-exception details) while keeping the user guidance (mentioning Update-PatServerToken, Get-PatStoredServer or explicit -Token); also adjust/update tests to assert the presence of the Update-PatServerToken guidance in both 401 message shapes if you retain the 'Unauthorized' clause.tests/Unit/Private/Invoke-PatApi.tests.ps1 (1)
116-131: Good fix on the WebException constructor and solid two-shape coverage.Switching to the 1-arg
[System.Net.WebException]::new(string)overload removes the silent constructor failure that was previously masking the assertion, and covering both the WebException-formatted message and the rawHttpRequestException-style "Response status code does not indicate success: 401 (Unauthorized)." string is exactly what's needed to lock down the regex inInvoke-PatApi.ps1line 137. Nice.One small suggestion: the second test (lines 124-131) only asserts on
*token is missing, expired, or invalid*. Consider also asserting on*Update-PatServerToken*there (as the first test does) so the cmdlet-naming contract is enforced for both message shapes — that way a future change to the regex that accidentally only matches one shape can't silently drop the cmdlet guidance for the other.♻️ Optional tighter assertion
{ Invoke-PatApi -Uri 'http://localhost:32400/test' -MaxRetries 1 -BaseDelaySeconds 0 } | - Should -Throw "*token is missing, expired, or invalid*" + Should -Throw "*token is missing, expired, or invalid*Update-PatServerToken*Get-PatStoredServer*" }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/Unit/Private/Invoke-PatApi.tests.ps1` around lines 116 - 131, Add an extra assertion to the second test ("Should include token recovery guidance when 401 is returned") so it also verifies the cmdlet guidance appears: when mocking the 401 string and invoking Invoke-PatApi, assert the thrown message contains both the token guidance "*token is missing, expired, or invalid*" and the cmdlet name "*Update-PatServerToken*" (same as the first test) to ensure the regex in Invoke-PatApi.ps1 still surfaces the cmdlet guidance for the alternate error message shape.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@PlexAutomationToolkit/Private/Invoke-PatApi.ps1`:
- Around line 137-144: The 401 branch in Invoke-PatApi is too broad and discards
diagnostic text; update the conditional to only trigger when the status
indicates 401 (e.g. $errorMessage -match '\b401\b') or when both a 401-like
status and the word "Unauthorized" are present (avoid a lone 'Unauthorized'
match), and modify the thrown message to append the original $errorMessage
(preserve URL/inner-exception details) while keeping the user guidance
(mentioning Update-PatServerToken, Get-PatStoredServer or explicit -Token); also
adjust/update tests to assert the presence of the Update-PatServerToken guidance
in both 401 message shapes if you retain the 'Unauthorized' clause.
In `@tests/Unit/Private/Invoke-PatApi.tests.ps1`:
- Around line 116-131: Add an extra assertion to the second test ("Should
include token recovery guidance when 401 is returned") so it also verifies the
cmdlet guidance appears: when mocking the 401 string and invoking Invoke-PatApi,
assert the thrown message contains both the token guidance "*token is missing,
expired, or invalid*" and the cmdlet name "*Update-PatServerToken*" (same as the
first test) to ensure the regex in Invoke-PatApi.ps1 still surfaces the cmdlet
guidance for the alternate error message shape.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: 12c64db5-a5b2-4cea-bdd7-95cbdcf38e8e
📒 Files selected for processing (2)
PlexAutomationToolkit/Private/Invoke-PatApi.ps1tests/Unit/Private/Invoke-PatApi.tests.ps1
Address review feedback on PR #39: - Drop the lone 'Unauthorized' regex clause; rely on \b401\b alone. Bare 'Unauthorized' could misfire on UnauthorizedAccessException, 403 bodies that include the word, proxy/auth middleware errors, etc., and then incorrectly recommend Plex token recovery. - Append 'Original error: <message>' to the 401 guidance so URL/inner- exception detail is retained for support and debugging. - Tighten the raw-string 401 test to also assert on Update-PatServerToken so the cmdlet-naming contract is enforced for both message shapes. - Add a regression test asserting that an UnauthorizedAccessException- style message does NOT trigger the 401 guidance. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
There was a problem hiding this comment.
🧹 Nitpick comments (1)
tests/Unit/Private/Invoke-PatApi.tests.ps1 (1)
116-131: Lock in the-Tokenrecovery-path contract in the 401 assertions.These tests validate cmdlet guidance, but they currently don’t assert the explicit
-Tokenfallback mentioned by the runtime message contract.Suggested assertion hardening
- { Invoke-PatApi -Uri 'http://localhost:32400/test' } | Should -Throw "*401 Unauthorized*Update-PatServerToken*Get-PatStoredServer*" + { Invoke-PatApi -Uri 'http://localhost:32400/test' } | Should -Throw "*401 Unauthorized*Update-PatServerToken*Get-PatStoredServer*-Token parameter*" ... - Should -Throw "*token is missing, expired, or invalid*Update-PatServerToken*Get-PatStoredServer*" + Should -Throw "*token is missing, expired, or invalid*Update-PatServerToken*Get-PatStoredServer*-Token parameter*"🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@tests/Unit/Private/Invoke-PatApi.tests.ps1` around lines 116 - 131, The 401-related tests must assert the explicit "-Token" recovery-path hint in the thrown guidance: update both Should -Throw expectations in the Invoke-PatApi tests to include the "-Token" fragment (e.g., ensure the first test's pattern contains "-Token" along with "401 Unauthorized", "Update-PatServerToken" and "Get-PatStoredServer", and likewise add "-Token" to the second test's pattern that already checks for "token is missing, expired, or invalid", "Update-PatServerToken" and "Get-PatStoredServer") so the tests lock in the -Token fallback contract for Invoke-PatApi.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Nitpick comments:
In `@tests/Unit/Private/Invoke-PatApi.tests.ps1`:
- Around line 116-131: The 401-related tests must assert the explicit "-Token"
recovery-path hint in the thrown guidance: update both Should -Throw
expectations in the Invoke-PatApi tests to include the "-Token" fragment (e.g.,
ensure the first test's pattern contains "-Token" along with "401 Unauthorized",
"Update-PatServerToken" and "Get-PatStoredServer", and likewise add "-Token" to
the second test's pattern that already checks for "token is missing, expired, or
invalid", "Update-PatServerToken" and "Get-PatStoredServer") so the tests lock
in the -Token fallback contract for Invoke-PatApi.
ℹ️ Review info
⚙️ Run configuration
Configuration used: defaults
Review profile: CHILL
Plan: Pro
Run ID: d9b8584b-ce05-44f1-9785-6bfe8e348312
📒 Files selected for processing (2)
PlexAutomationToolkit/Private/Invoke-PatApi.ps1tests/Unit/Private/Invoke-PatApi.tests.ps1
🚧 Files skipped from review as they are similar to previous changes (1)
- PlexAutomationToolkit/Private/Invoke-PatApi.ps1
The [Unreleased] section had drifted empty even though user-facing work landed since 0.10.3. Backfill the entries so the next release captures them and so users browsing the changelog can see what's coming. - Added: Update-PatServerToken (#31) — interactive/direct token refresh with vault or inline storage and post-update verification - Added: CI integration token validation pre-flight step (#31) — fast actionable failure when PLEX_TOKEN is expired - Changed: actionable 401 token recovery guidance from this PR (#39) Pure CI/dev-tooling churn (#34/#35/#36/#37/#38) intentionally omitted — Keep a Changelog focuses on user-facing changes. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Update-PatServerToken,Get-PatStoredServer, explicit-Token) instead of just surfacing the raw HTTP status.Invoke-PatApi, so every public cmdlet that hits the Plex API picks up the guidance automatically.[System.Net.WebException]with an invalid 4-arg overload — the constructor failure was masking the actual assertion, so the test was a false positive.Before
After
The outer
Failed to resolve section name:/Failed to get Plex library information:wrappers from upstream catch blocks are intentionally left alone — that's a separate readability issue worth addressing on its own.Test plan
pwsh -File ./build.ps1 -Task Test— full suite green (psake exit 0)Invoke-PatApiunit tests cover both the WebException path and the raw-message path*401*substring)🤖 Generated with Claude Code
Summary by CodeRabbit
Bug Fixes
Documentation
Tests