fix: address review follow-ups on API key sign-in error handling - #15312
fix: address review follow-ups on API key sign-in error handling#15312benceruleanlu wants to merge 2 commits into
Conversation
Report a denied (403) key once per key rather than on every launch. The key is deliberately kept, so the startup toast otherwise returns on every single app start, with no way to dismiss it short of deleting a key that is still valid. Drop the try/catch around trackAuthFailed: TelemetryRegistry.dispatch already isolates provider throws, and useAuthActions calls it bare. Stop the unverified message asserting the API could not be reached. It also covers 4xx/5xx responses and status-less AuthStoreErrors, where the API answered.
|
Important Review skippedAuto reviews are disabled on base/target branches other than the default branch. Please check the settings in the CodeRabbit UI or the ⚙️ Run configurationConfiguration used: Path: .coderabbit.yaml Review profile: ASSERTIVE Plan: Pro Plus Run ID: You can disable this status message by setting the Use the checkbox below for a quick retry:
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 |
🎭 Playwright: ✅ 1816 passed, 0 failed · 1 flaky📊 Browser Reports
🎨 Storybook: ✅ Built — View Storybook📦 Bundle: 8.85 MB gzipDetailsSummary
Category Glance App Entry Points — 3.71 kBMain entry bundles and manifests
Status: 1 added Graph Workspace — 1.37 MBGraph editor runtime, canvas, workflow orchestration
Status: 3 added Views & Navigation — 124 kBTop-level views, pages, and routed surfaces
Status: 17 added Panels & Settings — 565 kBConfiguration panels, inspectors, and settings screens
Status: 26 added User & Accounts — 27.7 kBAuthentication, profile, and account management bundles
Status: 11 added Editors & Dialogs — 125 kBModals, dialogs, drawers, and in-app editors
Status: 8 added UI Components — 67.1 kBReusable component library chunks
Status: 14 added Data & Services — 3.52 MBStores, services, APIs, and repositories
Status: 17 added Utilities & Hooks — 550 kBHelpers, composables, and utility bundles
Status: 38 added Vendor & Third-Party — 16.8 MBExternal libraries and shared vendor chunks
Status: 18 added Other — 14.2 MBBundles that do not match a named category
Status: 286 added ⚡ Performance Report
Absolute values
Raw data{
"timestamp": "2026-08-15T03:37:17.919Z",
"gitSha": "1b5a857bdb96d9f5afd05c490aa74246c9a6840c",
"branch": "fix/api-key-signin-followups",
"measurements": [
{
"name": "canvas-idle",
"durationMs": 2060.9059999999886,
"styleRecalcs": 10,
"styleRecalcDurationMs": 9.961,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 597.039,
"heapDeltaBytes": 4963532,
"heapUsedBytes": 69427920,
"domNodes": 20,
"jsHeapTotalBytes": 24641536,
"scriptDurationMs": 11.294000000000002,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "canvas-idle",
"durationMs": 2017.4389999999676,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.018999999999998,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 471.183,
"heapDeltaBytes": 4892308,
"heapUsedBytes": 69527348,
"domNodes": 18,
"jsHeapTotalBytes": 24641536,
"scriptDurationMs": 7.795,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1970.4600000000028,
"styleRecalcs": 74,
"styleRecalcDurationMs": 53.81399999999999,
"layouts": 12,
"layoutDurationMs": 4.361,
"taskDurationMs": 1024.7300000000002,
"heapDeltaBytes": -16075668,
"heapUsedBytes": 48421996,
"domNodes": -283,
"jsHeapTotalBytes": 23302144,
"scriptDurationMs": 124.784,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "canvas-mouse-sweep",
"durationMs": 1855.9739999999465,
"styleRecalcs": 76,
"styleRecalcDurationMs": 36.727000000000004,
"layouts": 12,
"layoutDurationMs": 3.3559999999999994,
"taskDurationMs": 879.0550000000001,
"heapDeltaBytes": -17428380,
"heapUsedBytes": 47111388,
"domNodes": -282,
"jsHeapTotalBytes": 23040000,
"scriptDurationMs": 105.13700000000001,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1760.387000000037,
"styleRecalcs": 31,
"styleRecalcDurationMs": 16.469,
"layouts": 6,
"layoutDurationMs": 0.649,
"taskDurationMs": 432.66700000000003,
"heapDeltaBytes": 8039012,
"heapUsedBytes": 72567252,
"domNodes": 74,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 13.770999999999999,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66999999999998,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "canvas-zoom-sweep",
"durationMs": 1733.0319999999801,
"styleRecalcs": 30,
"styleRecalcDurationMs": 17.924,
"layouts": 6,
"layoutDurationMs": 0.6809999999999999,
"taskDurationMs": 378.286,
"heapDeltaBytes": 7795792,
"heapUsedBytes": 72327452,
"domNodes": 76,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 10.781,
"eventListeners": 19,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 692.0369999999707,
"styleRecalcs": 11,
"styleRecalcDurationMs": 7.954000000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 418.275,
"heapDeltaBytes": -10998300,
"heapUsedBytes": 53474488,
"domNodes": 18,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 59.117,
"eventListeners": 2,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "dom-widget-clipping",
"durationMs": 590.5169999999771,
"styleRecalcs": 12,
"styleRecalcDurationMs": 9.26,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 389.226,
"heapDeltaBytes": -11124432,
"heapUsedBytes": 53465004,
"domNodes": 20,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 56.003,
"eventListeners": 0,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2024.9359999999683,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.251999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 626.995,
"heapDeltaBytes": 13446268,
"heapUsedBytes": 73512760,
"domNodes": -282,
"jsHeapTotalBytes": 2719744,
"scriptDurationMs": 17.145000000000003,
"eventListeners": -151,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-idle",
"durationMs": 2032.5319999999465,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.583,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 621.2260000000001,
"heapDeltaBytes": 12624028,
"heapUsedBytes": 72179632,
"domNodes": -282,
"jsHeapTotalBytes": 2457600,
"scriptDurationMs": 14.906000000000002,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "large-graph-pan",
"durationMs": 2143.5559999999896,
"styleRecalcs": 68,
"styleRecalcDurationMs": 14.447999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1136.39,
"heapDeltaBytes": 8065068,
"heapUsedBytes": 68889000,
"domNodes": -286,
"jsHeapTotalBytes": 3698688,
"scriptDurationMs": 295.26399999999995,
"eventListeners": -147,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "large-graph-pan",
"durationMs": 2161.948999999936,
"styleRecalcs": 69,
"styleRecalcDurationMs": 15.678999999999995,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 1196.4920000000002,
"heapDeltaBytes": -393460,
"heapUsedBytes": 60418408,
"domNodes": -283,
"jsHeapTotalBytes": 3698688,
"scriptDurationMs": 311.056,
"eventListeners": -179,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3281.516999999951,
"styleRecalcs": 64,
"styleRecalcDurationMs": 16.672000000000004,
"layouts": 60,
"layoutDurationMs": 9.261000000000001,
"taskDurationMs": 1540.47,
"heapDeltaBytes": 451280,
"heapUsedBytes": 62981324,
"domNodes": -287,
"jsHeapTotalBytes": 3244032,
"scriptDurationMs": 411.032,
"eventListeners": -153,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "large-graph-zoom",
"durationMs": 3165.1920000000473,
"styleRecalcs": 64,
"styleRecalcDurationMs": 15.448000000000004,
"layouts": 60,
"layoutDurationMs": 8.573999999999998,
"taskDurationMs": 1401.407,
"heapDeltaBytes": 16759100,
"heapUsedBytes": 79426024,
"domNodes": 10,
"jsHeapTotalBytes": 5505024,
"scriptDurationMs": 405.09900000000005,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2088.454000000013,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.93,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 690.5269999999999,
"heapDeltaBytes": 10874808,
"heapUsedBytes": 71723540,
"domNodes": -282,
"jsHeapTotalBytes": 2719744,
"scriptDurationMs": 22.096999999999998,
"eventListeners": -179,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "minimap-idle",
"durationMs": 2028.3719999999903,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.401000000000002,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 626.325,
"heapDeltaBytes": 12944816,
"heapUsedBytes": 73856780,
"domNodes": -286,
"jsHeapTotalBytes": 2457600,
"scriptDurationMs": 17.413000000000004,
"eventListeners": -149,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 640.8660000000168,
"styleRecalcs": 46,
"styleRecalcDurationMs": 10.476999999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 430.11699999999996,
"heapDeltaBytes": -10519420,
"heapUsedBytes": 54139048,
"domNodes": 18,
"jsHeapTotalBytes": 25952256,
"scriptDurationMs": 126.07499999999999,
"eventListeners": 8,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-dom-widget-clipping",
"durationMs": 632.6279999999542,
"styleRecalcs": 48,
"styleRecalcDurationMs": 11.986,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 417.62600000000003,
"heapDeltaBytes": -10384812,
"heapUsedBytes": 54208628,
"domNodes": 22,
"jsHeapTotalBytes": 26738688,
"scriptDurationMs": 124.02999999999999,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-idle",
"durationMs": 2062.1230000000423,
"styleRecalcs": 9,
"styleRecalcDurationMs": 8.139,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 624.9409999999999,
"heapDeltaBytes": 5065168,
"heapUsedBytes": 69653232,
"domNodes": 18,
"jsHeapTotalBytes": 25165824,
"scriptDurationMs": 11.458999999999998,
"eventListeners": 6,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "subgraph-idle",
"durationMs": 1994.5800000000418,
"styleRecalcs": 8,
"styleRecalcDurationMs": 7.442000000000003,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 464.478,
"heapDeltaBytes": 5001552,
"heapUsedBytes": 69707280,
"domNodes": 16,
"jsHeapTotalBytes": 25165824,
"scriptDurationMs": 7.642000000000003,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1733.4720000000061,
"styleRecalcs": 77,
"styleRecalcDurationMs": 38.919000000000004,
"layouts": 16,
"layoutDurationMs": 4.611000000000001,
"taskDurationMs": 816.959,
"heapDeltaBytes": -4031472,
"heapUsedBytes": 60754448,
"domNodes": 62,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 93.577,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.800000000000182
},
{
"name": "subgraph-mouse-sweep",
"durationMs": 1696.2210000000368,
"styleRecalcs": 75,
"styleRecalcDurationMs": 37.569,
"layouts": 16,
"layoutDurationMs": 4.874,
"taskDurationMs": 788.9399999999999,
"heapDeltaBytes": -3735780,
"heapUsedBytes": 60908064,
"domNodes": 60,
"jsHeapTotalBytes": 24903680,
"scriptDurationMs": 87.83399999999999,
"eventListeners": 4,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "subgraph-transition-enter",
"durationMs": 1401.8250000000307,
"styleRecalcs": 19,
"styleRecalcDurationMs": 29.048000000000005,
"layouts": 14,
"layoutDurationMs": 11.485,
"taskDurationMs": 933.204,
"heapDeltaBytes": -4037772,
"heapUsedBytes": 79157720,
"domNodes": 13673,
"jsHeapTotalBytes": 10223616,
"scriptDurationMs": 16.805,
"eventListeners": 2375,
"totalBlockingTimeMs": 122,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8440.98200000002,
"styleRecalcs": 249,
"styleRecalcDurationMs": 44.52199999999999,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4530.978,
"heapDeltaBytes": 14164296,
"heapUsedBytes": 74527736,
"domNodes": -282,
"jsHeapTotalBytes": 4222976,
"scriptDurationMs": 1040.327,
"eventListeners": -131,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333332,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "viewport-pan-sweep",
"durationMs": 8354.987000000052,
"styleRecalcs": 249,
"styleRecalcDurationMs": 42.02400000000001,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 4469.073,
"heapDeltaBytes": 19292568,
"heapUsedBytes": 79376604,
"domNodes": -282,
"jsHeapTotalBytes": 3960832,
"scriptDurationMs": 1086.915,
"eventListeners": -133,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "vue-large-graph-idle",
"durationMs": 18483.41099999999,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17416.418999999998,
"heapDeltaBytes": -32439076,
"heapUsedBytes": 165786484,
"domNodes": -8312,
"jsHeapTotalBytes": -13836288,
"scriptDurationMs": 127.127,
"eventListeners": -16387,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-idle",
"durationMs": 18552.021999999964,
"styleRecalcs": 0,
"styleRecalcDurationMs": 0,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 17492.113,
"heapDeltaBytes": -40785140,
"heapUsedBytes": 156825164,
"domNodes": -8312,
"jsHeapTotalBytes": -8593408,
"scriptDurationMs": 123.56299999999999,
"eventListeners": -16387,
"totalBlockingTimeMs": 0,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 22146.079999999984,
"styleRecalcs": 176,
"styleRecalcDurationMs": 20.903000000000006,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 21492.435999999998,
"heapDeltaBytes": -27087208,
"heapUsedBytes": 183922956,
"domNodes": -8312,
"jsHeapTotalBytes": -12591104,
"scriptDurationMs": 478.302,
"eventListeners": -16379,
"totalBlockingTimeMs": 161,
"frameDurationMs": 17.776666666666642,
"p95FrameDurationMs": 16.799999999999272
},
{
"name": "vue-large-graph-pan",
"durationMs": 22829.905999999937,
"styleRecalcs": 177,
"styleRecalcDurationMs": 23.090999999999973,
"layouts": 0,
"layoutDurationMs": 0,
"taskDurationMs": 22171.613,
"heapDeltaBytes": -36325888,
"heapUsedBytes": 161531008,
"domNodes": -8312,
"jsHeapTotalBytes": -12001280,
"scriptDurationMs": 491.642,
"eventListeners": -16381,
"totalBlockingTimeMs": 301,
"frameDurationMs": 17.773333333333238,
"p95FrameDurationMs": 16.700000000000728
},
{
"name": "workflow-execution",
"durationMs": 454.7279999999887,
"styleRecalcs": 11,
"styleRecalcDurationMs": 22.195,
"layouts": 4,
"layoutDurationMs": 1.456,
"taskDurationMs": 109.228,
"heapDeltaBytes": 4958008,
"heapUsedBytes": 68698888,
"domNodes": 120,
"jsHeapTotalBytes": 4718592,
"scriptDurationMs": 7.484000000000001,
"eventListeners": 99,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.666666666666668,
"p95FrameDurationMs": 16.699999999999818
},
{
"name": "workflow-execution",
"durationMs": 473.36200000006556,
"styleRecalcs": 11,
"styleRecalcDurationMs": 19.073,
"layouts": 3,
"layoutDurationMs": 0.7360000000000001,
"taskDurationMs": 111.05,
"heapDeltaBytes": 4933500,
"heapUsedBytes": 68674920,
"domNodes": 119,
"jsHeapTotalBytes": 4718592,
"scriptDurationMs": 7.582000000000002,
"eventListeners": 97,
"totalBlockingTimeMs": 0,
"frameDurationMs": 16.66333333333335,
"p95FrameDurationMs": 16.699999999999818
}
]
} |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 3a1ef19141
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| if (error.failure === 'denied') { | ||
| if (reportedDenialFor.value === apiKey.value) return | ||
| reportedDenialFor.value = apiKey.value |
There was a problem hiding this comment.
Record denials from interactive sign-ins
When a user initially submits a key and validation returns 403, isValidating makes the API-key watcher return before this block, while the form's error wrapper displays the denial without updating reportedDenialFor. The retained key therefore produces the same denial toast again on the next launch before this marker is finally written, contrary to reporting the verdict only once. Record an interactive denial against its key while still showing feedback for that submission.
Useful? React with 👍 / 👎.
Restores the clearer wording. The unverified case covers both a request that never got a response and one the API answered unhelpfully, so the message now splits on which happened rather than dropping the cause to avoid being wrong about the minority. Extracts the network-error predicate already inlined in toastErrorHandler rather than repeating the regex.
Codecov Report✅ All modified and coverable lines are covered by tests. @@ Coverage Diff @@
## glary/api-key-signin-error-handling #15312 +/- ##
====================================================================
Coverage 81.36% 81.36%
====================================================================
Files 1884 1884
Lines 109584 109593 +9
Branches 33222 33224 +2
====================================================================
+ Hits 89158 89167 +9
Misses 20040 20040
Partials 386 386
Flags with carried forward coverage won't be shown. Click here to find out more.
🚀 New features to boost your workflow:
|
|
Thank you |
Summary
Review follow-ups on #15272. Three independent changes; each stands alone if any is unwanted.
Changes
A denied (403) key is reported once, not on every launch. #15272 correctly keeps the key on a 403 — the key is good, the account is refused — but the startup watch then re-validates and re-toasts "API Key not permitted" on every single app start, with no way to dismiss it short of deleting a still-valid key. The verdict is now recorded against the key that produced it, so the user is told once. Changing the key, or the account regaining and then losing permission, reports again.
Dropped the try/catch around
trackAuthFailed.TelemetryRegistry.dispatchalready wraps every provider call in try/catch and logs (TelemetryRegistry.ts:73-81), and the existing caller inuseAuthActions.ts:35calls it bare. The guard, its comment, and the test that made telemetry throw were all covering a path that needs a non-registry dispatcher to reach."The Comfy API couldn't be reached" is now said only when that is what happened.
failureForroutes everything that is not 401/403 tounverified, which covers both a request that never got a response and one the API answered unhelpfully — 5xx, 429, and the two status-lessAuthStoreErrors fromcreateCustomer(authStore.ts:387,authStore.ts:420). Naming the network is the clearest thing to tell the user when it is true, so the message splits on which case it is instead of going vague for everyone. The predicate is the one already inlined intoastErrorHandler, now extracted rather than duplicated.Also trimmed three comments that restate their code.
Review Focus
comfy_api_key_denial_reportedis new persistent state. It is cleared whenever the key clears and whenever validation succeeds, so it cannot outlive the key it describes. An interactive 403 still always toasts — the user just asked, so they get an answer.The suppression test is red against #15272's store; the other new tests pin the marker to the key rather than to "seen a 403 once", and pin each unverified message to the failure that should produce it.
Note on merge order
Not addressed here, but worth settling before #15272 lands: Comfy-Org/cloud#6867 is still open. The 401 → discard-the-key decision assumes that backend change, and BE-7550's symptom is a fresh key 401ing for ~1 minute. Until it deploys, pasting a new key inside that window discards it and says "Invalid API Key" — the same user harm as the ticket, with an honest message. Not a regression (
maindiscards on any failure), but the headline symptom stays unfixed.