Skip to content

fix(mcp): exit the CLI cleanly on a broken-pipe stdout/stderr error - #8731

Merged
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
bitfathers94:fix/issue-8691
Jul 26, 2026
Merged

fix(mcp): exit the CLI cleanly on a broken-pipe stdout/stderr error#8731
loopover-orb[bot] merged 1 commit into
JSONbored:mainfrom
bitfathers94:fix/issue-8691

Conversation

@bitfathers94

Copy link
Copy Markdown
Contributor

fix(mcp): exit the CLI cleanly on a broken-pipe stdout/stderr error

process.stdout/stderr had no "error" listener, so a broken pipe (EPIPE) —
the downstream reader in ... | head closing after a few bytes while the
CLI is still writing a large payload — surfaced as an uncaught write EPIPE
and crashed the whole CLI with a stack trace. flushStdio() only drained via
the "drain" event, which never fires once the stream has errored.

Attach an "error" listener to both streams (gated on the launched-CLI
entrypoint) that exits cleanly: success on EPIPE (a normal end to a piped
command), a conventional failure code otherwise. Normal, fully-drained
output is unchanged.

Closes #8691

process.stdout/stderr had no "error" listener, so a broken pipe (EPIPE) —
the downstream reader in `... | head` closing after a few bytes while the
CLI is still writing a large payload — surfaced as an uncaught write EPIPE
and crashed the whole CLI with a stack trace. flushStdio() only drained via
the "drain" event, which never fires once the stream has errored.

Attach an "error" listener to both streams (gated on the launched-CLI
entrypoint) that exits cleanly: success on EPIPE (a normal end to a piped
command), a conventional failure code otherwise. Normal, fully-drained
output is unchanged.

Closes JSONbored#8691
@bitfathers94
bitfathers94 requested a review from JSONbored as a code owner July 26, 2026 01:19
@superagent-security

Copy link
Copy Markdown
Contributor

Superagent didn't find any vulnerabilities or security issues in this PR.

@codecov

codecov Bot commented Jul 26, 2026

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.29%. Comparing base (fac2222) to head (98e150d).

Additional details and impacted files
@@            Coverage Diff             @@
##             main    #8731      +/-   ##
==========================================
- Coverage   90.56%   82.29%   -8.27%     
==========================================
  Files          96       97       +1     
  Lines       22490    24750    +2260     
  Branches     3884     4742     +858     
==========================================
  Hits        20367    20367              
- Misses       1945     4205    +2260     
  Partials      178      178              
Flag Coverage Δ
backend 0.00% <ø> (?)

Flags with carried forward coverage won't be shown. Click here to find out more.

Files with missing lines Coverage Δ
packages/loopover-mcp/bin/loopover-mcp.ts 0.00% <ø> (ø)

@loopover-orb loopover-orb Bot added the gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier. label Jul 26, 2026
@loopover-orb

loopover-orb Bot commented Jul 26, 2026

Copy link
Copy Markdown
Contributor

Tip

✅ LoopOver review result - approve/merge recommended

Review updated: 2026-07-26 01:27:45 UTC

2 files · 1 AI reviewer · no blockers · readiness 93/100 · CI green · clean

✅ Suggested Action - Approve/Merge

  • safe to merge

Review summary
This adds an "error" listener on process.stdout/stderr, gated on the launched-CLI entrypoint, that exits 0 on EPIPE and 1 otherwise, fixing the uncaught-exception crash from a broken pipe (e.g. `| head`). The fix is correctly placed at the source (the streams themselves) rather than patching a symptom, and the added test spawns the real compiled CLI piped into a reader that closes early to reproduce the actual OS-pipe race rather than fabricating a payload. Placement right after flushStdio() and gating on runAsCliEntrypoint is consistent with the rest of the file's conventions for entrypoint-only side effects.

Nits — 4 non-blocking
  • The listener calls process.exit() directly from inside the error handler without draining any already-buffered writes on the other stream (e.g. an EPIPE on stdout could still race with a pending stderr write), though this is unlikely to matter in practice given the immediate process.exit semantics.
  • The 1705-line file continues to grow with this change; consider whether entrypoint-lifecycle concerns like this belong in a small dedicated module, per the file's own noted 1:1-conversion design debt.
  • Consider whether process.exitCode = ... plus a natural return would be safer than process.exit() inside an async-context error handler, though for this synchronous stream-error case it's likely fine.
  • The v8 ignore comment block is thorough and clearly explains why the branch is untestable in-process — good precedent to follow for other entrypoint-gated code in this file.

Decision drivers

  • ✅ Code review — No blockers (1 reviewer)
  • ✅ Gate result — Passing (No configured blocker found.)
Context & advisory signals — never blocks the verdict
Signal Result Evidence
Linked issue ✅ Linked #8691
Related work ✅ No active overlap found No same-issue or scoped active PR overlap found.
Change scope ✅ 20/20 Low review scope from cached public metadata (1 linked issue).
Validation posture ⚠️ 20/25 Preflight is ready, but the PR body does not name the validation run.
Contributor workload ✅ 10/10 Author activity: 44 registered-repo PR(s), 28 merged, 0 issue(s).
Contributor context ✅ Confirmed Gittensor contributor bitfathers94; Gittensor profile; 44 PR(s), 0 issue(s).
Improvement ✅ Minor risk: clean · value: minor · LLM: moderate
Linked issue satisfaction

Addressed
The diff attaches an "error" listener to both process.stdout and process.stderr that exits cleanly (0 on EPIPE, 1 otherwise) instead of crashing, gated on the CLI entrypoint, matching the fix location and requirement in the issue.

Review context
  • Author: bitfathers94
  • Role context: outside_contributor
  • Public audience mode: oss maintainer
  • Lane context: Repository is configured for direct PR review.
  • Public profile languages: TypeScript
  • Official Gittensor activity: 44 PR(s), 0 issue(s).
  • PR-specific overlap: none found.
Contributor next steps
  • Start here: Add validation command/output.
Signal definitions
  • Related work = same linked issue, overlapping active PRs, or title/path similarity.
  • Change scope = cached public metadata such as size labels, draft state, and review-burden hints.
  • Validation posture = whether the PR provides enough public validation/test evidence for maintainer review.
  • Contributor workload = public contributor activity and cleanup pressure, not a repo-wide quality failure.
  • Contributor context = public GitHub/Gittensor identity context; non-Gittensor status is not a blocker.
🧪 Chat with LoopOver

Ask LoopOver a question about this PR directly in a comment — grounded only in the same cached, public-safe facts shown above, never a new claim.

  • @loopover ask &lt;question&gt; answers contribution-quality Q&A with source citations and freshness.
  • @loopover chat &lt;question&gt; answers in natural prose from cached decision-pack facts via local inference (maintainer/collaborator; read-only).
  • A plain-language @loopover mention with a real question is routed to the closest matching read-only command automatically — no exact syntax required.

Full command reference: https://loopover.ai/docs/loopover-commands

🧪 Experimental — new and may change.

🟩 Safe / merged · 🟦 Advisory · 🟨 Held for review · 🟥 Blocked / closed


💰 Earn for open-source contributions like this. Gittensor lets GitHub contributors earn for the work they already do — register to start earning →.

Checked by LoopOver, a quiet PR intelligence layer for OSS maintainers.

  • Re-run LoopOver review

@loopover-orb loopover-orb Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LoopOver approves — the gate is satisfied and CI is green.

@loopover-orb
loopover-orb Bot merged commit 2fc27bc into JSONbored:main Jul 26, 2026
8 checks passed
@github-actions github-actions Bot mentioned this pull request Jul 26, 2026
12 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gittensor:bug Gittensor-scored bug fix — scores a 0.05x multiplier.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

fix(mcp): no error listener on stdout/stderr lets a broken pipe crash the CLI with an uncaught EPIPE exception

1 participant