fix(store): keep an unreadable proxy row from bricking every request - #422
Merged
Merged
Conversation
`8942287` renamed `ProxyMode::System` to `ProxyMode::Default`, changing the
persisted wire value from `"system"` to `"default"`. No migration rewrites
the existing `service_settings` row and `ProxyMode` has no alias, so any
install that ever saved proxy settings on an earlier build now holds a row
this build cannot deserialize:
unknown variant `system`, expected `default` or `custom`
`proxy_settings_secret` turned that into a hard error, and it is the first
statement of every outbound client factory, so on upgrade the failure hits
model calls, plugin installs, search, and the Cursor upstream proxy alike.
It is also unrecoverable from the UI. `proxy_settings` reads the same row,
so the settings page cannot render the proxy card, and `set_proxy_settings`
reads the existing row before it writes, so the user cannot overwrite the
row that broke them. Only editing SQLite by hand clears it.
Read the row through a fallback that logs and returns the default instead.
The affected rows are exactly the ones whose mode meant "no outbound proxy",
which is what the default already is, so nothing is silently changed for
them; a genuinely corrupt row costs the user a re-entered address instead of
a dead install.
Deliberately not `#[serde(alias = "system")]`: `8942287` added a test
asserting that value no longer parses, and this keeps that true while making
the persisted row survivable.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
The bug
8942287("refactor(proxy): change proxy mode fromsystemtodefault") renamed the enum variant:ProxyModeis#[serde(rename_all = "snake_case")], so the persisted wire value changed from"system"to"default". Nothing migrates the existing row, and there is no alias, so any install that ever pressed Save on the proxy card before this build holds aservice_settingsrow that this build cannot read:ProxyMode::Systemhas existed since the initial schema commit, so this is every upgrading install with saved proxy settings — including those that only ever left it on the default.Why it is worse than a bad proxy
proxy_settings_secretpropagated the error, and it is the first statement of every outbound client factory (network.rs:97,:112), whichprovider_client/cursor_client/default_clientall go through. On upgrade the read failure takes out model calls, plugin installs (plugin/installation.rs:65), search (search/fetch.rs:127,search/search_provider.rs:98) and the Cursor upstream proxy (api/cursor/proxy.rs).And it is unrecoverable from the UI, which is the part that made this worth a fix rather than a note:
proxy_settings()reads the same row, so the settings page cannot render the proxy card.set_proxy_settings()reads the existing row before it writes (it needs the old password whenpassword: None), so the Save that would replace the bad value fails on the bad value.Error::Jsonmaps to a 4xx, so the user sees a request error on a settings page and has no way forward short of editing SQLite by hand.The fix
Read the row through a fallback that logs and returns the default:
The rows this actually affects are the ones whose mode meant "no outbound proxy" — which is precisely what
ProxyMode::Defaultis — so nothing is silently changed for them. A genuinely corrupt row costs the user a re-entered address instead of a dead install, and the warning says why.Deliberately not
#[serde(alias = "system")].8942287addeddefault_proxy_mode_uses_the_default_wire_value, which assertsserde_json::from_str::<ProxyMode>("\"system\"").is_err(). This keeps that assertion true. If you would rather the old value keep its meaning exactly, the alias is a one-line change and I am happy to switch — that is your call on intent, not mine.Verification
cargo +1.95 test --package cursor-server --lib store::settingsBefore (fallback replaced with
.unwrap(), tests kept):After:
The second test is the one that matters: it writes the pre-rename row into a real store, then asserts both that
proxy_settings()still reads and thatset_proxy_settings()can overwrite it — i.e. that the settings page is reachable again.Gates (
make check, Rust half):cargo fmt --all -- --check— cleancargo clippy --workspace --all-targets -- -D warnings— clean on CI's stable; see notecargo test --workspace --all-targets— all suites greenToolchain note: this machine's
stableis broken, so the gates ran with+1.95. Clippy 1.95 reports acollapsible_matcherror atserver/src/cursor/compile/model.rs:109on unmodifiedmain; that is a 1.95-only false positive (its own suggestion,"fast" if parse_bool(parameter)? =>, does not compile —?is not allowed in a match guard) and CI's 1.98.1 does not emit it, somainis green. Clippy here therefore ran with-A clippy::collapsible_match; this diff is unaffected either way.Related
This is plausibly behind #404 ("changing network environment ... BYOK link drops, custom model cannot be selected, connectivity test also errors"), where the reporter's trigger is toggling proxy software — but I have not reproduced their setup, so treat that as a lead rather than a claim.