Skip to content

EXPERIMENTAL_BACKGROUND_SUBAGENTS: hidden sessions created inside ephemeral subagent processes can never be deleted at runtime (owner is undefined, the DELETE is never even attempted) #594

Closed

Description

@gaojunran

Short description

With OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true, OpenCode 2 spawns short-lived opencode processes for background subagents. These processes load Magic Context like any other opencode process. Hidden sessions created by the plugin running inside them record no owner, so they can never be deleted at runtime — by design of the owner-bound route, not by any transient failure.

What happened?

Every opencode process loads the configured plugins — we observed plugin boot logs from short-lived app: "cli" processes spawned for background subagents (four boots within one second during a batch). When the plugin inside such a process creates a hidden child:

  1. hostServiceOwner() only returns a value when the creating process registered a service. Ephemeral subagent processes never register, so child.owner is undefined.
  2. On retire, resolveOwnerHostService(undefined) throws HostServiceUnavailable ("created by an OpenCode host that registered no service, so there is no owner-bound route to delete it through") before any HTTP request is made.
  3. The failure is caught and logged as "left for a later sweep" — but the sweep hits the same gate, so runtime retries can never succeed. The plugin's own output says the only cleanup is doctor --fix with OpenCode closed.

Supporting observation: an ephemeral process booted at 01:33:47 observed the parent session idle at 01:34:24.524, and a batch of dreamer hidden sessions was created at 01:34:24.558 (34 ms later) — consistent with the plugin in the ephemeral process firing the due dream run.

Structurally, the creating process is gone by the time anyone could retry, so no owner-bound route can ever exist for these children — even on hosts where deletion otherwise works (e.g. OpenChamber attached to a registered service instance). This lane leaks one visible root session per hidden run until an offline doctor --fix pass, and on plain-serve hosts it adds to the pile from #589.

Suggestion

On retire, if the current process created the child and no owner-bound service route exists, attempt the delete while the process is still alive, through its own credentials: process.env.OPENCODE_PASSWORD (or the legacy OPENCODE_SERVER_PASSWORD) plus the hostname/port from process.argv (serve --hostname <h> --port <p>). Hosts that inject the password (OpenChamber does — it sets OPENCODE_PASSWORD on the managed instance) would then clean up at retire time. That same fallback also fixes the plain-serve case in #589. The fully general fix remains an in-process session remove in the plugin host API (anomalyco/opencode#49389).

Diagnostics

  • Affected children have no owner recorded; retirement logs "cannot be deleted: no owner-bound host removal route; run doctor --fix with OpenCode closed".
  • Code path: hostServiceOwner() → child.owner → resolveOwnerHostService gate (a) → throw before HTTP.

Plugin version

0.44.4 (dist analysis)

OpenCode version

2.0.18

Platform

linux x64

Client

OpenChamber 2.0.4 (web) against opencode serve; OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true

Activity

  1. magic-alfonso commented on Oct 1, 2026

    @magic-alfonso

    Thanks, that's an accurate trace. A child created inside a short-lived background-subagent process has no owner-bound route, so neither runtime retries nor the sweep can ever delete it.

    We'd rather not build the password-and-port fallback. The real fix has landed upstream: anomalyco/opencode#49389 is merged on OpenCode's v2 branch. It gives plugins in-process session.remove, and it lets session.create set a parent, so children are deleted together with their parent.

    We're building on that now. Each hidden run becomes a child of the session that triggered it, and it's removed in-process when the run finishes, so the creating process doesn't need a registered service. It works the same inside ephemeral subagent processes.

    It needs an OpenCode 2 release newer than 2.0.21, which doesn't include it yet. Once that release ships, we'll require it and remove the current workarounds. Until then, doctor --fix with OpenCode closed is the cleanup. I'll post here when it ships.

  2. magic-alfonso commented on Oct 4, 2026

    @magic-alfonso

    Fixed in 0.45.0, now on npm. On OpenCode 2.0.22 and newer, Magic Context creates its background sessions under your session and deletes them through OpenCode's own session API, so nothing reads credentials from the environment. Sessions earlier versions left behind are deleted in small batches at startup.

    To update, quit every OpenCode and Pi process first: the database migrates on first start, which can take up to a minute on a large database. OpenCode 1 and Pi pick up the new version on restart. On OpenCode 2 (2.0.22 or newer is now required), open /plugins, select Magic Context and press ctrl+u, or run opencode plugin update. If you still see this on 0.45.0, please reopen with a log.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions