Skip to content

fix(ssh-tunnel): ignore stderr stream errors during teardown - #100855

Merged
vincentkoc merged 5 commits into
openclaw:mainfrom
cxbAsDev:fix/ssh-tunnel-stderr-stream-errors
Jul 6, 2026
Merged

vincentkoc merged 5 commits into
openclaw:mainfrom
cxbAsDev:fix/ssh-tunnel-stderr-stream-errors

Conversation

@cxbAsDev

@cxbAsDev cxbAsDev commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

What Problem This Solves

startSshPortForward in src/infra/ssh-tunnel.ts reads ssh stderr for diagnostics but does not attach an "error" listener to the stderr stream. If stderr errors during ssh teardown (e.g. EPIPE after the child exits), the unhandled error can crash the gateway.

Why This Change Was Made

Added a no-op "error" listener on child.stderr. stderr is used only for diagnostic lines, so a stream error there is not actionable; swallowing it prevents an unhandled exception while the normal child error/exit paths still surface ssh-level failures.

User Impact

SSH tunnel teardown and gateway stability improve: stderr stream errors no longer propagate as unhandled exceptions.

Evidence

Regression test:

pnpm test src/infra/ssh-tunnel.test.ts
Test Files  1 passed (1)
     Tests  8 passed (8)

Real behavior proof:

node_modules/.bin/tsx scripts/proof/ssh-tunnel-stderr-stream-errors.mts
=== Proof: ssh-tunnel stderr stream error handling ===

Tunnel established on local port 43210.
Emitted stderr stream error.

PASS: stderr stream error was ignored and tunnel stopped cleanly.

@clawsweeper

clawsweeper Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Codex review: needs maintainer review before merge. Reviewed July 6, 2026, 11:59 AM ET / 15:59 UTC.

Summary
The branch adds an error listener to the SSH tunnel child stderr stream and regression coverage for active and teardown stream-error cases.

PR surface: Source +4, Tests +25. Total +29 across 2 files.

Reproducibility: yes. from source inspection. Current main observes stderr data without an stderr error listener, and Node's EventEmitter throws unhandled 'error' events when no listener is present.

Review metrics: none identified.

Merge readiness
Overall: 🦞 diamond lobster
Proof: 🦞 diamond lobster
Patch quality: 🦞 diamond lobster
Result: ready for maintainer review.

Overall follows the weaker of proof and patch quality, so missing proof can cap an otherwise strong patch.

Risk before merge

  • [P1] The latest maintainer proof comment names head 9a0cf5773566efe2a3c439d35e314aab7ee994d1, while the live head is cafce4e with expanded tests; merge should still wait for exact-head required checks such as QA Smoke CI to finish.

Maintainer options:

  1. Decide the mitigation before merge
    Merge the focused runtime and regression-test fix after the current head's required checks complete, preserving child error and exit as the authoritative SSH failure path.
  2. Pause or close
    Do not merge this PR until maintainers decide whether the risk is worth taking.

Next step before merge

  • No ClawSweeper repair lane is needed; the remaining action is maintainer merge after exact-head gates finish.

Security
Cleared: The diff adds no dependency, workflow, permission, secret, or new command-execution surface; it only observes a diagnostic stderr stream while preserving existing SSH child process failure handling.

Review details

Best possible solution:

Merge the focused runtime and regression-test fix after the current head's required checks complete, preserving child error and exit as the authoritative SSH failure path.

Do we have a high-confidence way to reproduce the issue?

Yes, from source inspection. Current main observes stderr data without an stderr error listener, and Node's EventEmitter throws unhandled 'error' events when no listener is present.

Is this the best way to solve the issue?

Yes. Best-fix verdict: best; observing only the diagnostic stderr stream is the narrowest maintainable fix because child error and exit events still report real SSH failures.

AGENTS.md: found and applied where relevant.

Codex review notes: model internal, reasoning high; reviewed against 8547682dd6f3.

Label changes

Label justifications:

  • P2: This is a focused gateway SSH tunnel stability bug fix with limited blast radius and no blocking review finding.
  • rating: 🦞 diamond lobster: Overall readiness is 🦞 diamond lobster; proof is 🦞 diamond lobster and patch quality is 🦞 diamond lobster.
  • status: 👀 ready for maintainer look: ClawSweeper has no concrete contributor-facing blocker left for this PR. Sufficient (terminal): The PR body includes terminal output from an after-fix SSH tunnel proof, the current head keeps focused regression coverage, and the Real behavior proof check passed for the live head.
  • proof: sufficient: Contributor real behavior proof is sufficient. The PR body includes terminal output from an after-fix SSH tunnel proof, the current head keeps focused regression coverage, and the Real behavior proof check passed for the live head.
Evidence reviewed

PR surface:

Source +4, Tests +25. Total +29 across 2 files.

View PR surface stats
Area Files Added Removed Net
Source 1 6 2 +4
Tests 1 25 0 +25
Docs 0 0 0 0
Config 0 0 0 0
Generated 0 0 0 0
Other 0 0 0 0
Total 2 31 2 +29

What I checked:

  • Repository policy read: Root AGENTS.md was read fully in two ranges and applied to require source, sibling, dependency-contract, and best-fix review before verdict. (AGENTS.md:1, 8547682dd6f3)
  • Current main behavior: Current main reads SSH stderr for diagnostics but has no stderr stream error listener before returning the tunnel handle, so a stream error can use Node's unhandled error path. (src/infra/ssh-tunnel.ts:168, 8547682dd6f3)
  • PR runtime fix: The PR keeps a local stderrStream reference, attaches a no-op error listener, and leaves child process error and exit handling in place as the authoritative SSH failure signal. (src/infra/ssh-tunnel.ts:168, cafce4e492f3)
  • Regression coverage: The PR test emits stderr stream errors while the tunnel is active and during teardown, and asserts the emit does not throw and stop resolves. (src/infra/ssh-tunnel.test.ts:209, cafce4e492f3)
  • Caller path: Gateway status starts the SSH port forward through startSshPortForward and always attempts tunnel.stop() in finally, so teardown-time stream errors affect a real gateway status path. (src/commands/gateway-status/probe-run.ts:70, 8547682dd6f3)
  • Sibling invariant: The shared process executor already treats stdout/stderr stream errors as non-authoritative while child exit/close remains authoritative, matching this fix shape. (src/process/exec.ts:615, 8547682dd6f3)

Likely related people:

  • cxbAsDev: Authored the merged SSH tunnel spawn-error fix in fix(ssh-tunnel): handle spawn error to prevent unhandled rejection crash #99800 and co-authored recent output-stream hardening work, so the same SSH tunnel failure surface is familiar history. (role: recent area contributor; confidence: high; commits: c6ed9d8d06a4, d1ec54cb60c3; files: src/infra/ssh-tunnel.ts, src/infra/ssh-tunnel.test.ts)
  • vincentkoc: Merged the prior SSH tunnel crash fix, is assigned on this PR, and added the intermediate regression-suite commit that removed the one-off proof script. (role: recent reviewer and merger; confidence: high; commits: c6ed9d8d06a4, 9a0cf5773566; files: src/infra/ssh-tunnel.ts, src/infra/ssh-tunnel.test.ts)
  • steipete: Authored and merged the recent subprocess output-stream hardening PR and added the latest test expansion on this PR branch. (role: adjacent owner; confidence: high; commits: a4b032e5d710, cafce4e492f3; files: src/process/exec.ts, src/agents/sessions/exec.ts, src/infra/ssh-tunnel.test.ts)
What the crustacean ranks mean
  • 🦀 challenger crab: rare, exceptional readiness with strong proof, clean implementation, and convincing validation.
  • 🦞 diamond lobster: very strong readiness with only minor maintainer review expected.
  • 🐚 platinum hermit: good normal PR, likely mergeable with ordinary maintainer review.
  • 🦐 gold shrimp: useful signal, but proof or patch confidence is still limited.
  • 🦪 silver shellfish: thin signal; proof, validation, or implementation needs work.
  • 🧂 unranked krab: not merge-ready because proof is missing/unusable or there are serious correctness or safety concerns.
  • 🌊 off-meta tidepool: rating does not apply to this item.

Shiny media proof means a screenshot, video, or linked artifact directly shows the changed behavior. Runtime, network, CSP, and security claims still need visible diagnostics.

How this review workflow works
  • ClawSweeper keeps one durable marker-backed review comment per issue or PR.
  • Re-runs edit this comment so the latest verdict, findings, and automation markers stay together instead of adding duplicate bot comments.
  • A fresh review can be triggered by eligible @clawsweeper re-review comments, exact-item GitHub events, scheduled/background review runs, or manual workflow dispatch.
  • PR/issue authors and users with repository write access can comment @clawsweeper re-review or @clawsweeper re-run on an open PR or issue to request a fresh review only.
  • Maintainers can also comment @clawsweeper review to request a fresh review only.
  • Fresh-review commands do not start repair, autofix, rebase, CI repair, or automerge.
  • Maintainer-only repair and merge flows require explicit commands such as @clawsweeper autofix, @clawsweeper automerge, @clawsweeper fix ci, or @clawsweeper address review.
  • Maintainers can comment @clawsweeper explain to ask for more context, or @clawsweeper stop to stop active automation.
Review history (4 earlier review cycles)
  • reviewed 2026-07-06T11:55:55.043Z sha a2f940da59fe8be325c8140fffad28ac71644414 :: needs real behavior proof before merge. :: none
  • reviewed 2026-07-06T14:21:15.714Z sha 1bd24c334ca02eb29382e74275ce50144aacf672 :: needs changes before merge. :: [P3] Stop returning from the Promise executor
  • reviewed 2026-07-06T15:10:31.444Z sha c5f455e95f3a00986fd061c17f992f159539bd9f :: needs maintainer review before merge. :: none
  • reviewed 2026-07-06T15:19:30.543Z sha c5f455e95f3a00986fd061c17f992f159539bd9f :: needs maintainer review before merge. :: none

@clawsweeper clawsweeper Bot added rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. P2 Normal backlog priority with limited blast radius. labels Jul 6, 2026
@openclaw-barnacle openclaw-barnacle Bot added scripts Repository scripts size: S and removed size: XS labels Jul 6, 2026
@cxbAsDev
cxbAsDev force-pushed the fix/ssh-tunnel-stderr-stream-errors branch from c811108 to 1bd24c3 Compare July 6, 2026 14:05
@clawsweeper clawsweeper Bot added proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. and removed rating: 🦪 silver shellfish Thin PR readiness signal; proof, validation, or implementation needs work. status: 📣 needs proof The PR needs real behavior proof before ClawSweeper can clear the contributor ask. rating: 🦐 gold shrimp Decent PR readiness signal, but merge confidence is limited. labels Jul 6, 2026
@cxbAsDev
cxbAsDev force-pushed the fix/ssh-tunnel-stderr-stream-errors branch from c5f455e to 4ea5ebb Compare July 6, 2026 15:29
@vincentkoc vincentkoc self-assigned this Jul 6, 2026
@openclaw-barnacle openclaw-barnacle Bot added size: XS and removed scripts Repository scripts size: S labels Jul 6, 2026
@steipete
steipete force-pushed the fix/ssh-tunnel-stderr-stream-errors branch from 9a0cf57 to cafce4e Compare July 6, 2026 15:49
@vincentkoc

vincentkoc commented Jul 6, 2026

Copy link
Copy Markdown
Member

Maintainer review complete for exact head cafce4e492f396e2785668a8a6f5024297783f30.

I kept the runtime fix, removed the 76-line one-off proof script, and retained the author's improved table-driven regression coverage for both active-use and teardown timing. The diagnostic stderr pipe is observed without changing the authoritative child error/exit lifecycle.

Validation:

No remaining proof gaps. Ready to squash merge.

@vincentkoc
vincentkoc merged commit 32cc9b0 into openclaw:main Jul 6, 2026
96 checks passed
@vincentkoc

Copy link
Copy Markdown
Member

Merged via squash.

@steipete

steipete commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Maintainer repair is land-ready at cafce4e492f396e2785668a8a6f5024297783f30.

  • Before: an SSH diagnostic stderr stream error could be emitted without an error observer and terminate the process.
  • After: the diagnostic pipe is observed before encoding/data listeners while child events remain the tunnel authority.
  • Repair: deleted the proof-only script and made active-use versus teardown timing explicit in the regression.
  • Local lightweight checks: git diff --check; targeted formatting was verified with oxfmt.
  • Exact-head proof: full hosted CI is green: https://github.com/openclaw/openclaw/actions/runs/28804406855
  • Fresh autoreview: no accepted/actionable findings.

Known gap: no external SSH server was used; the regression directly exercises both lifecycle phases around the owned child stream.

@steipete

steipete commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Merged via squash.

github-actions Bot pushed a commit to Desicool/openclaw that referenced this pull request Jul 7, 2026
…w#100855)

* fix(ssh-tunnel): ignore stderr stream errors during teardown

* chore(proof): add real behavior proof for ssh-tunnel stderr stream errors

* chore(proof): fix promise executor lint in ssh-tunnel proof

* test(ssh-tunnel): keep stream error proof in regression suite

* test(ssh): cover active stream errors

---------

Co-authored-by: Vincent Koc <vincentkoc@ieee.org>
Co-authored-by: Peter Steinberger <steipete@gmail.com>
@cxbAsDev
cxbAsDev deleted the fix/ssh-tunnel-stderr-stream-errors branch July 15, 2026 03:33
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

P2 Normal backlog priority with limited blast radius. proof: sufficient ClawSweeper judged the real behavior proof convincing. rating: 🦞 diamond lobster Very strong PR readiness with only minor maintainer review expected. size: XS status: 👀 ready for maintainer look ClawSweeper has no concrete contributor-facing blocker left for this PR.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants