diff --git a/CHANGELOG.md b/CHANGELOG.md index 5af702c459..77142b1f4c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -3,6 +3,10 @@ ## [Unreleased] +### Changed + +- Removing a provider key now surfaces the server's specific CSRF rejection reason ("Session expired - reload the page", "Cross-origin mismatch - check reverse proxy headers", or the fallback "Cross-origin request rejected") when the underlying POST is rejected with 403, instead of swallowing all three into one generic toast. (Refs #2572) + ## [v0.51.137] — 2026-05-25 — Release DI (stage-batch19 — 6-PR medium-risk batch) ### Added diff --git a/static/panels.js b/static/panels.js index fedd8301a9..1943ebae8c 100644 --- a/static/panels.js +++ b/static/panels.js @@ -6874,7 +6874,19 @@ async function _removeProviderKey(providerId){ if(els.saveBtn){els.saveBtn.disabled=false;els.saveBtn.textContent=t('providers_save');} } }catch(e){ - showToast('Error: '+e.message); + // A 403 from /api/providers/delete fires when the CSRF cookie/header + // pair has drifted. The server distinguishes three reasons in + // api/routes.py:_csrf_rejection_error ("Session expired - reload the + // page", "Cross-origin mismatch - check reverse proxy headers", and + // the fallback "Cross-origin request rejected"); api()'s catch lifts + // that string onto e.message. Pass it through verbatim so the + // deployment-shape failure #2572 calls out keeps its actionable hint + // instead of being flattened to a single generic toast. + if(e&&e.status===403){ + showToast(e.message||'Session expired. Reload the page and try again.',6000,'error'); + }else{ + showToast('Error: '+e.message); + } if(els.saveBtn){els.saveBtn.disabled=false;els.saveBtn.textContent=t('providers_save');} } }