Skip to content

[Bug] Location inactivity eviction SIGTERMs live background shells after 60 minutes #51828

Open

Description

@KamitobiHaru

Description

Description

Background shells are SIGTERM'd when LocationActivity evicts their
location after 60 minutes without session events. Service stays alive
(same PID); only the location-scoped Shell/Pty graph is torn down by
locations.invalidate().

Timeline 2026-09-27 (service 2.0.10): location services evicted directory=/data3/zsy/asr2report_pipeline 16:24:48.527Z → torchrun
Received 15 death signal 16:24:48.563Z → payload handler rc=143, signal=15. No service restart near the window.

Steps to reproduce

  1. serve, create a background shell with a long command
    (POST /api/shell, e.g. sleep 4000), then leave the location idle
    (no session events).
  2. Wait past the 60-minute TTL: the sweep expires the entry and calls
    locations.invalidate(), killing the child.

Root cause

The sweep only exempts locations owning active SessionExecution
sessions (location-activity.ts). A backgrounded shell/pty emits no
session events and holds no execution claim, so it looks idle.

Plugins

No response

OpenCode version

No response

Steps to reproduce

No response

Screenshot and/or share link

No response

Operating System

No response

Terminal

No response

Activity

  1. github-actions commented on Sep 28, 2026

    @github-actions
    Contributor

    This issue might be a duplicate of existing issues. Please check:

    • #48691: Reports terminals killed when a location is evicted after 60 minutes; the same inactivity eviction tears down live shell/PTY processes.
    • #51343: Reports 60-minute location eviction interrupting a still-running session and background work; the underlying location-activity eviction behavior is closely related.
  2. SamSpiri commented on Oct 3, 2026

    @SamSpiri

    Second independent instance of background-shell completion loss (v2.0.22)

    Independent confirmation of the exact symptom, with the completion notification proven absent from storage.

    Session ses_efeb61f29ffeWnthm3xB8kevap, background shell job sh_10280cd3f001TXnljvbScsSUtv (launched 18:02:34). Session ended its turn at 18:03:05 (last durable event in the location). Location idle TTL elapsed ~19:03; the job actually finished 19:11:33. No notification was ever delivered; session was woken manually at 20:11.

    • No synthetic row with that shellID anywhere in session_message — if delivery had happened the append-only row would exist.
    • session_inbox / session_pending empty for the session — rules out "admitted but wake dropped".
    • No server restart in the window (NRestarts=0), so restart recovery did not run.
    • .out file removed, matching the watcher's interrupt finalizer (tool/plugin/shell.ts:234).
    • Control: a second background: true job in the same session ran ~17 min and delivered a synthetic ({source:"shell", shellID, jobID, state:"completed", exit:0}) normally.

    Root mechanism: ShellTool.notifyWhenDone is Effect.forkIn(scope, …) into the location/plugin scope (tool/plugin/shell.ts:154-186); the LocationActivity sweep interrupts owners and calls locations.invalidate(ref) (location-activity.ts:51,77-81), closing that scope and interrupting the watcher before sessions.synthetic(...) runs. The job process itself survives (detached), so the command completes with nowhere to report.

    Detail for matching duplicates: the deadline is idle time since the last durable event in the location (1-min sweep), not job runtime — any background: true job expected to outlive ~60 min of location inactivity loses its completion notification.

    (Also posted a longer version on #52908.)

  3. TonyYanS commented on Oct 9, 2026

    @TonyYanS

    Independent confirmation on 2.0.24: live shell terminated and automatic summary lost

    Environment

    • OpenCode: 2.0.24, latest channel; source baseline e7a34f09bfd9134dfade5a8ddb843f7030bc9a69.
    • OS: Darwin 27.0.0, arm64.
    • Terminal: Ghostty; shell: /bin/zsh.
    • The installed client has two local UI-only patches (composer visibility and location-less forms); the affected Core lifecycle code was unchanged when observed.
    • Production has two local plugins for continuity metadata and session-history archival. The accelerated reproduction used an isolated profile without those plugins, synthetic data and no external model calls.

    Observed behavior

    A read-only observation command was launched through the agent's shell tool with background: true and timeout: 0, intended to run for 75 minutes. It wrote its own timestamps and an interruption receipt.

    Redacted timeline (UTC, 2026-10-09):

    00:48:47.109  location services evicted directory=<workspace>
    00:48:47.143  observation receipt: interrupted, signal=SIGTERM
    00:48:47.178  GET /api/shell/<shell-id>/output -> 404
    

    The observation ran for 3,700.6 seconds and retained 62 samples. Its on-disk summary survived, but the owning session did not automatically receive a completion summary. The eviction and 404 share the same server run ID; this event was Location eviction, not a server restart.

    Reproduction

    1. Ask the agent to start a harmless long-running shell, e.g. sleep 4500; printf 'BACKGROUND_DONE\n', with background: true and timeout: 0.
    2. Let the agent become idle and avoid further durable Session activity in that Location.
    3. Wait beyond the 60-minute Location idle TTL and sweep interval.
    4. Observe Location eviction, loss of shell observability, and the missing automatic completion notification.

    The threshold is time since the Location's last relevant activity, not a fixed timeout measured from process startup.

    Source analysis and local verification

    In the tested baseline, packages/core/src/location-activity.ts only refreshed activity from durable Session events. The background Shell and ShellTool.notifyWhenDone observer have Location-owned lifetimes. Eviction can tear down both while the owning session is idle.

    An isolated local candidate checks the already-cached Shell service for running commands before idle eviction, refreshes activity on shell creation/exit, and rechecks the activity entry before invalidation. This is supporting evidence, not a claim that either upstream PR has been tested or merged.

    Validation:

    • Accelerated real-child reproduction: 1-second Location TTL, 3-second command. Unpatched success/nonzero cases lost the Location before completion.
    • Patched success, nonzero exit, timeout and explicit cancellation each admitted a notification and woke the original session once; notification markers were acknowledged and ordinary idle eviction resumed afterward.
    • 67 lifecycle/restart/Job/Shell tests passed; Core typecheck and root bun run check passed.
    • Compiled candidate, isolated service and loopback model fixture: background command -> synthetic completion notice -> model wake -> automatic assistant summary passed for exit 0 and exit 7.
    • Six additional macOS bash-related tool-shell tests failed identically on the unchanged baseline; these are not claimed as passing.

    The candidate has not been activated in production, and a full-length production soak of it is still pending. Actual server-restart recovery (resume: false for idle shell owners) is a separate behavior, not addressed by this idle-eviction change.

    Related pending fixes: #52580 and #51827. Please include completion-notification delivery/owner wake, not only child-process survival, in regression coverage.

  4. AlexeyShirshov commented on Oct 14, 2026

    @AlexeyShirshov

    Cross-link: #55305 asks for the 60-minute location TTL to be configurable (or opt-out).

    Same root cause as this report — LocationActivity's deadline expires an idle location, and the eviction tears down the location-scoped Shell/Pty graph. If the exemption logic discussed here lands, the knob may still be wanted for hosts that intentionally stay idle for long periods (an agent waiting on a human answer), which is what #55305 covers. Other surfaces of the same deadline: #51343 (running session interrupted), #48691 (terminals killed).

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

Metadata

Metadata

Assignees

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