LearnNewsExamplesServices
Frontmatter
id10638
titleCapture osascript stderr in bridge-daemon.mjs
stateClosed
labels
bugairegression
assigneesneo-gemini-3-1-pro
createdAtMay 3, 2026, 3:18 PM
updatedAtMay 3, 2026, 4:12 PM
githubUrlhttps://github.com/neomjs/neo/issues/10638
authorneo-gemini-3-1-pro
commentsCount0
parentIssuenull
subIssues[]
subIssuesCompleted0
subIssuesTotal0
blockedBy[]
blocking[]
closedAtMay 3, 2026, 4:12 PM

Capture osascript stderr in bridge-daemon.mjs

Closedbugairegression
neo-gemini-3-1-pro
neo-gemini-3-1-pro commented on May 3, 2026, 3:18 PM

Context

A recent bridge-daemon restart revealed failures in wake subscription delivery via osascript. The failures were functionally opaque (logging only "osascript exited with code 1") because the bridge-daemon suppressed standard error output. This observability gap compounded the regression introduced during the manage_wake_subscription rollout.

The Problem

Because stderr was ignored in the spawnAsync utility within bridge-daemon.mjs (via stdio: 'ignore'), the actual macOS TCC (Transparency, Consent, and Control) error (error 1002, osascript is not allowed to send keystrokes) was hidden from the logs. This made the initial diagnostics blind and delayed root cause identification, forcing reliance on manual probes to discover the OS-level permission block.

The Architectural Reality

In ai/scripts/bridge-daemon.mjs, the spawnAsync wrapper executes system commands (like osascript and tmux) but discards all output streams using { stdio: 'ignore' }. While stdout isn't needed for these asynchronous fire-and-forget commands, discarding stderr destroys critical diagnostic forensics when commands fail with non-zero exit codes.

The Fix

Modify spawnAsync in ai/scripts/bridge-daemon.mjs:

  • Change stdio configuration to { stdio: ['ignore', 'ignore', 'pipe'] }.
  • Capture child.stderr.on('data') chunks.
  • Append the captured and trimmed stderr string to the Error object message when the process exits with a non-zero code.

Acceptance Criteria

  • spawnAsync captures stderr output instead of ignoring it.
  • Rejected promises from spawnAsync include the stderr text (if present) alongside the exit code.
  • No regression in successful command execution or silent operation when exit code is 0.

Out of Scope

  • Fixing the underlying macOS TCC permissions (this is an operational host-level concern).
  • Addressing the MCP cache staleness or Codex whitelist gaps detailed in #10636.

Avoided Traps

  • Trap: Piping stderr to process.stderr directly instead of capturing it. Resolution: Capturing and embedding it in the Error object ensures the existing catch blocks in deliverDigest can properly format and log the failure contextually within the daemon's log file format, rather than causing unformatted stream pollution.

Origin Session ID: d28a63c7-a4ec-40ff-aaaa-62d862e031f5 Retrieval Hint: query_raw_memories("bridge-daemon.mjs stderr stdio ignore osascript exit code 1")

tobiu referenced in commit fcc06ba - "fix(ai): capture osascript stderr in bridge-daemon.mjs (#10638) (#10639) on May 3, 2026, 4:12 PM
tobiu closed this issue on May 3, 2026, 4:12 PM