[Bug]: Agents are denied preview when another project has an agent browser access override#23085

opened3d agobyQqainguin

Note

Filed by Claude Code (Haiku 5.5) at the request of the reporter. The reproduction and root cause were checked with unit tests on main.

Before submitting

  • I searched existing issues and did not find a duplicate. The closest is #17202, which quotes the same denial message but reports the opposite symptom (tools visible while access is off).
  • I included enough detail to reproduce or investigate the problem.

Area

apps/server

Steps to reproduce

  1. Keep "Agent browser access" on globally (the default).
  2. In Settings, turn "Agent browser access" on for one project, so that project has a per-project browser override. Leave a second project with no browser override.
  3. Start a Claude Agent thread in the second project.
  4. Ask the agent to call preview_status or preview_open.

Expected behavior

The second project has no override, so it inherits the global setting (on). The preview tools work.

Actual behavior

Every preview call fails at once:

text
1MCP credential does not grant the preview capability: browser preview tools are off for this thread. Do not retry them. To check a page, use a headless browser from the shell, such as Playwright, or curl. The user can turn on "Agent browser access" in Settings; it applies when the agent session next starts.

Suspected cause: ProviderSessionManager.agentAccessSettings (apps/server/src/orchestration-v2/ProviderSessionManager.ts) sees a browser override anywhere on the server, so it looks up the thread's project through Effect.serviceOption(ProjectService). layerProduction in apps/server/src/orchestration-v2/runtimeLayer.ts does not supply ProjectService to the session manager, so the lookup returns None and the code takes the fail-closed branch. Every thread is denied, including threads that inherit the global "on".

Reproduced at main e78f580468 with a local, uncommitted unit test that builds RuntimeLayer.layerProduction:

text
1FAIL production MCP policy inherits enabled access with an unrelated project override
2AssertionError: expected false to equal true
3FAIL production MCP policy honors a project opt-in with an unrelated project override
4AssertionError: expected false to equal true

A local change that provides layerProjectService to the whole production layer group makes all five policy cases pass. It has not been pushed or opened as a PR.

Impact

Major degradation or frequent failure

Version or commit

main @ e78f580468 for the test and root cause. The version of the desktop build the reporter runs is not known.

Environment

Linux (Omarchy 4.0.6). Provider: Claude Agent. The agent reports the denial in several threads and projects.

Logs or stack traces

The denial appears as the tool_use_result of every mcp__t3-code__preview_* call in the affected threads, starting 09:57 on 2026-10-21 and continuing through the day.

Workaround

Remove the per-project "Agent browser access" override from every project that has one. Without any browser override left, the server uses the global setting. This follows from reading the code. It has not been confirmed in the Settings UI.

Related

  • #17202: same denial message and setting, opposite symptom.
Synced from GitHub

Additional evidence from t3 triage on 2026-10-23: the same denial occurs with the Codex provider on macOS, including in a fresh thread whose own project explicitly enables browser access.

Environment

  • Running local, desktop-managed T3 Code Nightly server: 0.1.3-nightly.20261023.3566, verified against the live server version endpoint.
  • macOS arm64, Darwin 24.6.0.
  • Provider: Codex. The exact frontend surface used for the original calls was not independently established.
  • Triage CLI: 0.1.3-nightly.20261023.3566.
  • Release source inspected at tag v0.1.3-nightly.20261023.3566, commit 807319c276c6831b2c219640e9da5b38019a8f1d.

Observed behavior and evidence

  1. The affected project's projectSettingsOverrides[projectId].enableAgentBrowserAccess is true. Its legacy projectAgentBrowserAccessOverrides[projectId] is also true. The global field is omitted and defaults to true in this release.
  2. The settings file was last modified on October 19, before the affected threads were created on October 23. This does not appear to be a setting enabled after session startup.
  3. Persisted tool results show preview_status({}) denied about 32 seconds after a fresh thread was created, at 11:23:50 UTC. Another thread in the same project contains three equivalent preview_open denials.

Expected: the enabled project policy permits preview calls. Actual:

text
1MCP credential does not grant the preview capability: browser preview tools are off for this thread.

Diagnosis and limits

This confirms an enabled-policy/authorization mismatch and strongly matches this issue. In the inspected release, ProviderSessionManager captures ProjectService as an optional dependency; when browser overrides exist, a failed project lookup takes the browser: false path before applying the override. Credential creation then omits the preview capability.

There is some ProjectService provisioning in the release's main runtime branch, so the precise live dependency-construction cause remains an inference. This investigation did not inspect the live service context or independently rebuild the production layer in a test. The persisted denial and saved settings are confirmed; the proposed wiring explanation is not independently proven here.

Tool visibility despite denied authorization also overlaps #17202. No settings or source changes were made, and the override-removal workaround was not tested. No raw logs, credentials, private URLs, or thread/project identifiers are included here.


Investigated using t3 triage --agent codex: OpenAI Codex CLI 0.164.0, model gpt-6-astra, medium reasoning effort (verified from the triage session metadata). Comment prepared and posted at the reporter's request by Codex in T3 Code, model gpt-6.1-sol, high reasoning effort.

Synced from GitHub

Note

Written by Claude Code (Fable 5.1) at the reporter's request, from a live investigation on the reporter's machine.

The same defect also denies the device tools, with a different message, and the override-removal workaround is confirmed to work.

Environment

  • T3 Code (Beta) desktop, server 0.1.2 (stable, not nightly), macOS arm64, Darwin 27.0.0.
  • Provider: Claude Agent. Device hub expo-device-hub 0.15.3 and agent-device 0.21.25 installed and reported as running.
  • Settings: enableDeviceSupport: true, enableAgentDeviceAccess: true globally, plus one project with projectSettingsOverrides[<id>].enableAgentDeviceAccess: true (set through the Settings UI; the row showed the reset arrow). No browser overrides anywhere.

Observed

Every fresh thread in that same project, including a brand-new probe thread started after both toggles were on and both tools installed, got:

text
1Error: Agent device access is turned off for this environment.

from mcp__t3-code__device_list and device_open. That is requireDeviceAccess mapping the missing device capability in apps/server (requireThreadMcpCapability("device")).

Evidence that the project lookup never runs

In server.trace.ndjson, the ProviderSessionManagerV2.agentAccessSettings span for the probe thread's session open has exactly two children, serverSettings.materializeSettingsSecrets and ProjectionStore.getThread, and no ProjectService.getById child, even though a device override exists. The sibling McpSessionRegistry.issue then mints a credential without device. That matches the serviceOption(ProjectService) → None → fail-closed branch described above, now seen on the device path (deviceOverridden ? false : ...).

Workaround confirmed

Deleting enableAgentDeviceAccess from projectSettingsOverrides[<id>] in settings.json (global toggle left on) was picked up by the settings watcher without a restart. A fresh probe thread in the same project then got a full device_list result (local host ready, both tools at required versions, simulators listed). Existing sessions keep their old credential, so only new sessions recover.

Suggestion for #23116

Since agentAccessSettings resolves browser and device in the same call, the fix there should cover this too. It may be worth adding a device-override case to the new production-layer regression so the device path is pinned as well.