Skip to content

Silent deadlock when a second opencode instance starts in the same git project #50348

Description

@amtx

Description

Related: #35870 (headless hang at startup, ZIO lost-wakeup hypothesis), #38201 (same project copy refresh started signature, large binaries), #48848 (cross-process gap in snapshot/git ops). None of these cover the repro below: a second concurrent instance for the same git project deterministically stalls one of them, and the wait is observable as a zombie git rev-parse whose JS await never resumes.

Version: opencode 1.18.31 (standalone binary, embeds Bun v1.3.14)
OS: Linux 6.1.0-42-amd64 (Debian), x86_64, running as root
Repro rate: consistent while a first instance is alive for the same repo

Summary

If one opencode instance (e.g. a TUI session) is running for a git project, starting a second instance for the same project hangs forever, silently: no response, no error, no timeout, no log output. The hang only occurs when the project contains a .git directory; non-git projects are unaffected.

Reproduction

# terminal 1 (stays open)
cd /path/to/any-git-repo
opencode            # TUI, idle is enough

# terminal 2
cd /path/to/any-git-repo   # or any subdirectory, or a linked worktree of the same repo
opencode run "say hi"      # hangs forever — no output whatsoever
  • Works: second instance in a directory without .git.
  • Works: second instance in a different git repo.
  • Fails: same repo (common dir), including from subdirectories / worktrees.

Observed behavior (failing instance)

  • opencode run blocks indefinitely; process main thread parked in rt_sigsuspend (permanent await).
  • Log ends at project copy refresh started (projectID=…), after init count=5. No process / stream events ever appear.
  • DB: the session is created but stays at 0 tokens, 0 parts, no finishReason.
  • No outbound network, no TCP port bound, no lock held on the opencode DB (external BEGIN IMMEDIATE writes succeed during the hang).
  • Killing the first instance (or waiting for it to exit) lets the blocked one proceed.

Evidence gathered (strace -f, /proc, binary analysis)

  • During refresh, opencode runs a fixed sequence of git coprocesses. One of them — git rev-parse --git-common-dir — is left as a zombie: it has exited 0, wait4 succeeds in the trace, yet the JS fiber awaiting it never resumes.
  • Strace shows repeated re-spawns of the same command ~1 s apart (timeout/retry loop on a fiber that never completes), the main thread blocked on futex, and the worker pool expanding (45× clone3(CLONE_THREAD)) until nothing progresses.
  • The same command run manually completes in ~6 ms. Raw Bun coprocess spawning (git rev-parse, 3 concurrent, stdin:"ignore") works fine in Bun 1.3.13.
  • Bisected the project directory contents: .git is the sole trigger (a dir with only AGENTS.md or only opencode.json does not hang).
  • The hang reproduces with a completely fresh XDG_DATA_HOME/XDG_STATE_HOME/HOME (not state-dependent), and with strace -f attached (which adds overhead) it fails more often than without — consistent with a race.
  • MCP servers, auth, and the message path were all verified healthy and are not involved (failure predates any model/network activity).

Expected behavior

A second instance for the same project should either:

  1. fail fast with a clear message (opencode is already running for this project (pid …, cwd …)), or
  2. report/attach to the existing session.

A silent infinite hang with exit-code ambiguity is the worst failure mode: the process must be killed by hand.

Workaround

Keep exactly one opencode session per git repository (or per repo common dir, since worktrees share it). Guard script used here: /usr/local/bin/opencode.sh — scans running opencode processes' /proc/*/cwd, resolves the git common dir, and refuses to start a second instance for the same project.

Hypothesis (labeled as such)

The refresh pipeline runs its git coprocesses through the shared Bash service; the completion of one of those child processes is lost (child exits and is reaped at OS level, but the JS-side await never fires), and a retry loop keeps re-spawning without ever completing — i.e. a coprocess-completion notification race in the runtime/service layer, worsened (but not caused) by concurrency. Confirming/fixing the actual mechanism is left to the maintainer; the evidence above narrows it to the coprocess await during project refresh.

Plugins

No response

OpenCode version

1.18.31

Steps to reproduce

No response

Screenshot and/or share link

No response

Operating System

No response

Terminal

No response

Activity

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