Note
Grok responding on behalf of Julius.
Thanks for the detailed report and the isolated reproduction. The double decode checks out on main @ a6ec88f7a.
What's happening
requiredScopesForSettingsUpdaterunsSchema.decodeUnknownSync(SettingsUpdate)(payload)inside theRpcScopeAuthorizationmiddleware (apps/server/src/auth/RpcAuthorization.ts:227-240, called from:259and:266-271). By then, the RPC server has already decodedpayloadagainst the same struct, which is theserverUpdateSettingspayload schema (packages/contracts/src/rpc.ts:737-741).BackgroundActivityOverridesusesSchema.DurationFromMillisfor all five timers (packages/contracts/src/settings.ts:1128-1133). The top-levelautomaticGitFetchInterval/providerHealthRefreshIntervalinServerSettingsPatchuse it too (settings.ts:1747-1748). The second decode getsDurationvalues where it expects millisecond numbers and throws. I reproduced this with a minimal struct on effect 4.0.1 (the version in the workspace catalog): the first decode succeeds, the second throwsSchemaError: Expected number at ["patch"]["backgroundActivity"]["overrides"]["automaticGitFetchInterval"], and a boolean-only override patch decodes twice without error.backgroundActivityOverrideSettingsalways copies every resolved timer intooverrides. That's why flipping any Advanced toggle, including Pause when locked, sends Durations and hits this.
Why the connection drops (likely)
The throw is synchronous inside the middleware function, not an Effect.fail. In effect 4.0.1's RpcServer, middleware is applied inside handleRequest, so the throw appears to reach the Effect.catchDefect around write and is sent as a connection-level Defect instead of a per-request exit. On the client, RpcClient handles Defect with clearEntries(Exit.die(...)), which fails every pending request and open stream on that socket. That fits the connectionLost → reconnect you saw. The dialog closing is probably a knock-on effect of the environment briefly disconnecting, but I didn't trace that UI path end to end.
Scope
- This most likely started with #9786 (merged 2026-10-06), which added the
SettingsUpdatere-decode. Before that,serverUpdateSettingshad a fixed scope (AuthOrchestrationOperateScope) and its payload wasn't decoded again. The existing tests inRpcAuthorization.test.tsonly send patches without Durations, so they didn't catch it. - It probably also affects the Git fetch and provider health check interval inputs and their reset buttons, which go through the same helper. Restore defaults in General likely hits it too, since it sends the top-level Duration fields. I haven't tested those individually.
assetsCreateUrlandgitPreparePullRequestThreaduse the same "decode the payload again in the middleware" pattern. Their schemas look idempotent at a glance, so I don't think they're affected today, but I haven't verified that.
Possible fix (untested)
requiredScopesForServerSettingsPatchonly looks at which keys are defined (settings.ts:1830-1846), and the payload is already the decoded type. So the middleware could treatpayloadastypeof SettingsUpdate.Type, or check it with a type-side guard, instead of running the wire decoder again.- Separately, it may be worth having the middleware turn any unexpected throw into a typed failure, so a bug in scope calculation fails one request instead of the whole connection.
- A regression test would help: an
RpcTest.makeClientcall throughRpcAuthorization.layer([AuthSettingsWriteScope])with abackgroundActivity.overridespatch that contains a Duration.
Related
I didn't find a duplicate or an open fix PR. #15131 (MCP preferences tool missing ThreadCommandExecutor) is a different path. #13235 and #12000 are about how the Git fetch and provider health intervals behave, not about saving them.
Workaround until a fix lands: choosing a preset profile should still save, since it sends overrides: {}. As you noted, though, none of them turns off Pause when locked.