Repository navigation
Description
Description
Several read-only API endpoints currently run through LocationMiddleware or SessionLocationMiddleware, which always calls LocationServiceMap.get. For an unloaded Location, observing metadata or checking ephemeral state therefore constructs and retains the complete Location runtime, including filesystem search, watchers, plugins, MCP processes, catalogs, and VCS services.
An instrumented comparison demonstrated the amplification:
- Polling pending Forms and Permissions for 18 historical Locations created 18 complete runtime graphs, 72,364 server inotify watches, and a 140-process tree.
- With the polling client closed, no historical graphs were built; the server retained 14 watches and a 7-process tree.
The polling client should not have enumerated historical Locations, but passive reads should not prepare every workspace requested by an authenticated client.
The proposed invariant is:
- Durable metadata reads use the database or lightweight Location/Project resolution without constructing a runtime.
- Runtime-owned collection reads use an existing runtime and return an empty collection when none exists.
- Runtime-owned item reads use an existing runtime and return their current not-found error when none exists.
- An existing in-progress runtime build is joined and retained for the request scope.
- Creation, execution, runtime-derived catalogs/configuration, and explicit preparation may continue to construct runtimes.
The affected metadata reads are:
location.get, project.current, project.list, permission.saved.list, and session.instructions.entry.list.
The affected existing-runtime reads are:
form.request.list, permission.request.list, session.form.list, session.form.get, session.form.state, session.permission.list, session.permission.get, pty.list, pty.get, shell.list, shell.get, and shell.output.
Existing-runtime reads require an atomic, scoped optional acquisition operation. Effect-TS/effect#7411 (released in 4.0.0-rc.112, we are using rc.111 right now) adds RcMap.getOption and LayerMap.contextEffectOption for this purpose. Missing entries return Option.none without building a layer, while ready or in-progress entries are retained for the caller scope.
OpenCode can wrap LayerMap.contextEffectOption as LocationServiceMap.useIfPresent. It should not compose RcMap.has and get, because TTL expiry or invalidation can remove an entry between those operations and cause get to construct a new runtime.
The optional existing-runtime portion of this fix depends on Effect PR #7411 being merged, released, and adopted by OpenCode.
Acceptance criteria:
- Calling any affected endpoint for an unloaded Location does not add a
LocationServiceMapentry. - Collection endpoints return their existing empty response shape when runtime state is absent.
- Item endpoints preserve their current not-found response.
- Missing Sessions still return
SessionNotFoundError. - Existing and in-progress runtimes continue to expose their current state correctly.
- The calls do not start filesystem search, watchers, plugins, MCP processes, catalogs, or VCS services.
- Runtime-demanding endpoints retain their current behavior.
Filesystem/VCS capability decomposition, catalog/config semantics, runtime TTL policy, Integration attempts, mutations, and explicit prewarming are out of scope.
Plugins
@onshape-mcp/opencode-auth and local ~/.config/opencode/plugins/claude-plugin.js. The reproduction is at the authenticated HTTP API boundary and is not plugin-specific.
OpenCode version
Local V2 build opencode2 v0.0.0-v2-202608222007 from commit bdec7a4f87a6a29d37dee975716273a40cbc77a3.
Steps to reproduce
- Start a V2 server and identify a valid project directory that is absent from
GET /api/debug/location. - Call
GET /api/permission/requestorGET /api/form/requestwith that directory inlocation[directory]. - Call
GET /api/debug/locationagain. - Observe that the read added a complete Location runtime.
- Inspect the server's child processes and
/proc/<pid>/fdinfoinotify counts. - Repeat with several unloaded directories and observe one retained runtime, associated watchers, and child processes per directory.
- Compare with a run where those reads are not made.
The same behavior can be observed with the other listed routes by querying unloaded Locations or Sessions whose Location runtime is not active.
Screenshot and/or share link
No share link. This is server runtime behavior; the instrumented resource comparison is included above.
Operating System
Ubuntu Linux x86_64, kernel 7.0.0-29-generic.
Terminal
xterm-256color. The reproduction uses authenticated HTTP requests and is not terminal-specific.
Activity
altendky commentedon Aug 31, 2026
Status update: #45994 addressed the primary resource-amplification scenario demonstrated here.
Session-family hydration of pending forms and permissions now uses atomic optional acquisition for
session.form.listandsession.permission.list, returning an empty collection for an unloaded Location rather than constructing its runtime. The special global-form list path received the same treatment. This prevents polling historical sessions from creating the runtime graphs, watchers, plugins, MCP processes, and child-process growth described in the reproduction.The broader passive-read invariant and endpoint audit in this issue remain incomplete. In particular, the listed metadata reads, location-wide pending-request reads, individual form and permission reads, PTY reads, and shell reads have not all been converted to lightweight or existing-runtime-only acquisition.
Accordingly, #45994 resolves the primary observed failure mode but only partially resolves this issue's expanded scope.
altendky commentedon Aug 31, 2026
but hey, in favor of a clean issue list i'll close this. thanks!