Skip to content

opencode run --agent X "prompt" returns empty stdout AND stderr when spawned via direct execvp (no shell) — v1.15.0 regression #27669

Closed as not planned

Description

@CyberFreedomHolding

Summary

In opencode v1.15.0 (macOS, darwin-arm64), opencode run --agent <user-agent> "<prompt>" exits cleanly with code 0 but emits 0 bytes on both stdout and stderr when the process is spawned via direct execvp (e.g. Python subprocess.run(argv, shell=False), or posix_spawn/execve directly).

The exact same argv invoked via a shell (Python subprocess.run(cmd, shell=True), or sh -c "...", or interactive bash) produces the expected agent response on stdout. This appears to be a regression introduced in v1.15.0, most likely tied to the "Effect-based core event system" change mentioned in the release notes.

This breaks any automation that spawns opencode run without going through a shell — CI scripts, launchd jobs, MCP-style headless integrations, etc.

Environment

  • opencode: v1.15.0 (installed at ~/.opencode/bin/opencode via opencode's own install script)
  • OS: macOS 15.x (Darwin 25.5.0), arm64
  • Reproducer language: Python 3.12.x

No third-party plugins active. opencode.jsonc loads cleanly (opencode agent list returns 13 agents).

Reproducer (minimal)

# repro.py
import subprocess
OC = "/path/to/.opencode/bin/opencode"
AGENT = "your-agent-name"
PROMPT = "Reply with the single word: ok"

# Case A — direct execvp via argv list. EXPECTED: agent response on stdout. ACTUAL: 0 bytes.
proc = subprocess.run(
    [OC, "run", "--agent", AGENT, PROMPT],
    capture_output=True, text=True, timeout=120,
)
print(f"execvp: rc={proc.returncode} stdout={len(proc.stdout)}b stderr={len(proc.stderr)}b")

# Case B — same argv via shell=True. WORKS.
import shlex
shell_cmd = " ".join(shlex.quote(a) for a in [OC, "run", "--agent", AGENT, PROMPT])
proc = subprocess.run(
    shell_cmd, shell=True,
    capture_output=True, text=True, timeout=120,
)
print(f"shell:  rc={proc.returncode} stdout={len(proc.stdout)}b stderr={len(proc.stderr)}b")

Expected output:

execvp: rc=0 stdout=>0b stderr=>0b
shell:  rc=0 stdout=>0b stderr=>0b

Observed output on v1.15.0:

execvp: rc=0 stdout=0b stderr=0b
shell:  rc=0 stdout=<N>b stderr=<M>b   # N, M > 0, includes the agent response

The exit code is 0 in both cases — the failure is silent.

Full matrix observed

Tested with and without --dangerously-skip-permissions to rule out a flag interaction (initial hypothesis). The flag is irrelevant; only the spawn path matters:

Invocation --dangerously-skip-permissions stdout stderr
Python subprocess.run(argv, shell=False) absent 0 B 0 B
Python subprocess.run(argv, shell=False) present 0 B 0 B
Python subprocess.run(cmd, shell=True) absent 117 B 54 B
Python subprocess.run(cmd, shell=True) present 135 B 54 B
Interactive bash opencode run --agent X "prompt" either works works

All cells exit 0. Trials reproduced across 3 runs each per cell.

Workaround

For automation, route through the shell:

import shlex, subprocess
shell_cmd = " ".join(shlex.quote(a) for a in argv)
subprocess.run(shell_cmd, shell=True, capture_output=True, text=True)

Or call a sh/bash -c "..." wrapper directly.

Suspected cause

v1.15.0 release notes mention:

Added an Effect-based core event system for more complete event delivery across sessions and integrations.

The same release also restored "missing event types in the JavaScript SDK." It looks plausible that the new event system has a code path where stdout writes are gated on a signal (parent process, TTY heuristic, session-init handshake) that is satisfied when spawned via a shell but missed when spawned via direct execvp. I haven't bisected internally — happy to test a patch against my reproducer.

Severity / impact

  • Silent failure with exit 0 is the worst-case shape for CI/automation: the caller has no signal that anything went wrong, and downstream pipelines proceed on empty input.
  • Affects launchd jobs, MCP hosts, gh workflow invocations, post-commit hooks — anything spawning opencode run without an intermediate shell.
  • Workaround is one-line, so impact is mitigatable, but the silent shape means the bug can ship into production unnoticed until a downstream step rejects empty input (which may itself fail silently).

Related release-note context

From v1.15.0:

  • Core / Improvements: "Added an Effect-based core event system for more complete event delivery across sessions and integrations."
  • SDK / Bugfixes: "Restored missing event types in the JavaScript SDK, including session and message events."

The pairing suggests event-routing changes that may have broken a non-shell stdout path.


Filed by an operator using opencode in headless automation pipelines; happy to provide additional diagnostics, a dtruss capture, or test patches against the reproducer.

Activity

  1. github-actions commented on May 15, 2026

    @github-actions
    Contributor

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

  2. github-actions commented on Jul 15, 2026

    @github-actions
    Contributor

    To stay organized issues are automatically closed after 60 days of no activity. If the issue is still relevant please open a new one.

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