Repository navigation
Server update buttons silently do nothing for connections paired before the environment:maintain split #17034
Description
Activity
Note
Grok responding on behalf of Julius.
Thanks for the detailed report. I checked it against
mainat d720210, and the diagnosis holds.Confirmed on main
sessionGrantsScopereturnssession.permissions.includes(scope)whenever the server sendspermissions(L186). ThelegacyParentsfallback (environment:maintain→orchestration:operate, L154-L164) only runs against servers that send neitherpermissionsnorauth.serverUpdateScope.- Upgraded servers send the stored grant as-is.
getSessionStatespreadsauthScopeResponse(session.scopes), which setspermissionsto the stored scopes. fix(auth): keep old clients connected across scope changes #10298, which landed with feat(auth): separate environment administration permissions #9786, deliberately removed scope expansion ("Existing credentials keep their recorded grants"). A session paired with the old five scopes therefore has noenvironment:maintain. - The server would refuse the update anyway.
RpcAuthorization.tsL77-L79 requiresenvironment:maintainforserver.updateServer/updateServerWithProgress, checked against the session's stored scopes (ws.ts L3159). So the client-side gate itself is consistent with the server. - Banner / row button:
canUpdatefeedsdisabled={manualCommand === null && !canUpdate}(L333, and L316 for the icon variant). Nothing explains why it's disabled. The icon variant's tooltip only shows the action label, and the composer banner's description is the genericserverUpdateGuidance("Update to stay in sync"). - "Update all":
savedServerUpdateTargetsdoesn't look at the session's permissions.ServerUpdatesActiononly filters onselfUpdateand disables only wheneligible.length === 0(L159). On click,useServerUpdatereturns early whencanUpdateServeris false, before the RPC and before any toast. That matches the silent no-op you saw. - The only hint is
PermissionUpdateNotice. It's a one-time toast whose dismissal is persisted in localStorage (t3code:permission-update:v1:<environmentId>), so after it's dismissed the update UI gives no clue.
Root cause
The permission split (#9786 / #10298) intentionally leaves pre-split sessions without
environment:maintain, and the update UI gates on that permission without telling anyone. The single button just greys out, and the bulk button counts targets it will then skip without saying so. Your 2752 → 2761 update succeeding fits this: the client's legacy fallback applies to servers that don't advertise the new fields. I haven't verified exactly which release tags carry which commits.Possible fixes (untested)
- Surface the reason (safe part). When the button is disabled because
environment:maintainis missing, show something like "This connection needs to be paired again to update the server", ideally with an "Open Connections" action. Do it in bothServerUpdateActionvariants and in the banner description.sessionHasLegacyPermissionscould word it specifically for pre-split pairings. - Make "Update all" honest. Either leave targets without
environment:maintainout ofsavedServerUpdateTargets(so the button hides or disables when none qualify), or have the click toast which servers were skipped and why, instead of returning silently. - Letting old sessions update again would mean treating
orchestration:operateas grantingenvironment:maintainfor legacy grants. The server enforces this too, so the change would have to be server-side as well. That widens what existing credentials can do and reverses an explicit decision in fix(auth): keep old clients connected across scope changes #10298, so it's a security/product call for the maintainers, not a straightforward bug fix.
Workaround
Pair the environment again with a fresh link (Settings → Connections → Add Environment with the new link or code; see remote-access.md). Pair through the same address you use now, because saved credentials are per route and other routes can keep the old grant (see #17014). Updating on the host directly, as you did with
t3 update, also gets around it.Related
- fix(web): disable server update Button until connected #6204 (open, currently conflicting) adds a disabled-state tooltip to
ServerUpdateAction, but only for "not connected yet". It doesn't cover permissions, though the same tooltip pattern could carry this reason. - [Bug]: t3-code running as service reports not connected - have to re-pair #13072 may involve the same gate for older Tailscale pairings. Not confirmed.
- [Bug]: iOS Provider accounts fails with missing providers:manage on fresh T3 Connect tokens #16804 is the same family (missing
providers:manage), but on freshly issued mobile sessions, so it's a different cause. - [Bug]: Local environment can't be turned back on once disabled (toggle hidden behind "No environment selected") #17013 is another settings regression from feat(auth): separate environment administration permissions #9786, with an unrelated mechanism.
- [Bug]: incompatible environments hide app/server update guidance #15835 is a different update-guidance gate (incompatible environments).
- addedbugSomething is broken or behaving incorrectly.Something is broken or behaving incorrectly.via-triageFiled through npx t3 triageFiled through npx t3 triage
on Oct 8, 2026 Additional data point from
t3 triage: the browser side panel is also greyed out for the same reason, not only the update button.Environment
- Client: T3 Code desktop,
0.0.46-nightly.20261008.2801(d720210), Windows 11 x64 - Remote: a saved remote environment ("agentic-gui"), paired before the permission split
Symptoms
- The server update nudge for the remote shows, but its Update button is disabled, with no explanation.
- The browser/preview side panel for threads on that remote is disabled too, also with no explanation.
Diagnosis (same mechanism)
The preview UI is gated onpreview:operate(AuthPreviewOperateScope), for example inPreviewPanel.tsxL37,LegacySidebar.tsxL409,ChatMarkdown.tsxL2581,ElectronBrowserHost.tsxL130 and_chat.tsxL40. Likeenvironment:maintain, it is a child oforchestration:operateinlegacyParents.sessionGrantsScopenever falls back to that parent once the server returnspermissions, so pre-split pairings lose the browser panel as well as server updates, and nothing in the UI says why.Suggestion
Whatever fix lands for the update button's disabled reason (fix 1 above) should also cover the preview/browser panel entry points and any other control gated on a child scope. A shared "needs re-pairing for permission X" hint driven bysessionHasLegacyPermissionswould cover all of them. The one-timePermissionUpdateNoticetoast is easy to miss or dismiss.Workaround: run
npx t3@nightly pairon the host (the default standard scopes includeenvironment:maintainandpreview:operate), then use Add Environment with the new link through the same route.Comment drafted by Claude Opus 5.5 (Claude Code) via
t3 triage.- Client: T3 Code desktop,
What happened
The macOS desktop app (2787) connects to a remote Linux server (2761) that runs as the systemd background service. There are two update entry points, and neither works:
There is no indication of why. The server's 2752 → 2761 update, triggered from the same Mac the day before, worked.
Diagnosis
The client blocks the update because the connection's session lacks
environment:maintain. That is intended. The bug is that the UI does not say so: one button is disabled with no explanation, and the other is enabled but does nothing.auth_sessionsrow for it has these scopes:["orchestration:read","orchestration:operate","terminal:operate","review:write","relay:read"].The row is labelled "MacBook Air", client version 2787, and its
last_connected_atmatches the websocket that carried the clicks./api/auth/sessionreturnspermissionsequal to the stored scopes, sosessionGrantsScope(session, "environment:maintain")(packages/contracts/src/auth.ts:181-192) takes thepermissionsbranch at:186and returns false. The legacyorchestration:operate → environment:maintainfallback only applies whenpermissionsis absent. This is why the 2752 → 2761 update worked and every update after it does not.apps/web/src/components/ServerUpdateAction.tsx):canUpdate = canUpdateServer(sessionState)at:226, thendisabled={manualCommand === null && !canUpdate}at:333.manualCommandis null becauseselfUpdate === "boot-service". The button is greyed out with no reason given.savedServerUpdateTargets(apps/web/src/components/settings/ConnectionsSettings.tsx:2148-2181) filters on version mismatch, enabled, connected, not desktop-local and a non-nullselfUpdate, but not on the session's permissions.ServerUpdatesAction(ServerUpdateAction.tsx:125-159) therefore counts the server as eligible and keeps the button enabled. On click,useServerUpdate(:70-76) runsif (!canUpdateServer(...) || pending) return;before callingserver.updateServer, so nothing is sent and nothing is shown.RpcAuthorization.ts:77-79requiresenvironment:maintainforserver.updateServerandserver.updateServerWithProgress. The background service, launcher and runtime download were not involved. Runningt3 updateby hand on the server succeeded right away.PermissionUpdateNotice.tsxshows a one-time "Permissions have changed for …" toast for legacy-only sessions. Its dismissal is stored in localStorage, and after that the update UI gives no hint that re-pairing is needed.The same code is unchanged on
mainas of 2026-10-08.ServerUpdateAction.tsxwas last touched by 365aa87 (#10298).Relevant files at the 2787 tag:
apps/web/src/components/ServerUpdateAction.tsxapps/web/src/components/settings/ConnectionsSettings.tsxpackages/contracts/src/auth.tsSteps to reproduce
ws.rpc.server.updateServer*span. The only traffic isserver.reportClientActivityfrom the click.Expected: either the session can still update its server, or both buttons explain that the connection needs re-pairing for
environment:maintain. "Update all" should not count targets it will skip, and should not skip them silently.Version
Client: 0.0.46-nightly.20261007.2787 (macOS desktop). Server: 0.0.46-nightly.20261007.2761, systemd user service with launcher 0.0.46-nightly.20261003.2610.
Environment
Server: CachyOS Linux x64 (kernel 7.2), Node v26.8.2, systemd user service. Client: macOS desktop app (Electron 44.4.2) over a direct Tailscale route.
Evidence
Related issues
providers:manage), but on mobile and for provider auth.Fix applied or workaround
Ran
npx t3@nightly update --yeson the server, which moved it to 0.0.46-nightly.20261008.2801 and restarted the service cleanly. The Mac was deliberately left on its old pairing so the bug can still be reproduced on the next version mismatch. The permanent workaround is to re-pair the connection with a fresh link (t3 auth pairing create/t3 pair), which issuesAuthStandardClientScopesincludingenvironment:maintain.Filed by
Claude Code (claude-opus-5-5) via
t3 triage