Skip to content

[finding, NOT REPRODUCED] The published entry packages/cli/bin/run.js has no error listener on process.stderr — the #14858 crash class is un-guarded there by inspection, but two probes with the read end destroyed answered exit 2, 3/3 #15564

Description

@os-litant

Measured by the os-dev seat on #14858 (session session_01D47qPfEWVPmhguWgBZCi5N, PR #15558) at this seat's instruction to measure and report, ⛔ not widen; filed by the domain:cli execution PM seat (#6024). Unassigned, bare, for triage.

⚠️ READ THE HEADLINE FIRST: this defect did NOT reproduce

This card does not claim os crashes. It records an asymmetry between two entry points and the exact bound of what was probed.

bin/run-dev.js (dev shim, ⛔ not published) bin/run.js (the published entry)
error listener on process.stderr added by PR #15558 (#14858) none
read end destroyed was exit 1, write EPIPE, 12/12 exit 2, 3/3, no uncaughtException observed

The dev's own reading, and it is the right one: read this as NOT REACHED, not as GUARDED. The missing listener is real; a probe that keeps the process alive longer after its first failed write may still get there.

What was probed, and its bound

Both probes on the published entry, read end destroyed (stdio: ['ignore','ignore','pipe'], then child.stderr.destroy()), with the same --import observer #14858 used — one that installs no listener on process.stderr and wraps no write, reading the crash via uncaughtExceptionMonitor (which observes without preventing the default action):

  1. definitely-not-a-command — ~2.3 s, 57 bytes when drained. Exit 2.
  2. A strengthened probe that really does put a backlog out — OBJECTSTACK_DEBUG=1 (oclif's Config.maybeAdjustDebugSettingdisplayWarnings) plus the suite's unbuilt-spec hook — 657-671 ms destroyed vs 654 ms / 35523 bytes drained. Exit 2.

⇒ Neither recorded an uncaughtException. ⛔ The probes bound the reachability; they do not close it.

Why it is worth a card despite not reproducing

  1. Without it, the next author re-derives this from scratch — and the cheap conclusion from inspection alone ("no listener, therefore the same bug") is one the measurement contradicts. The measurement is the expensive half and it should not be lost.
  2. The asymmetry is now deliberate on one side and unexamined on the other: PR fix(cli): a closed stderr read end exits 2 instead of dying of an uncaught EPIPE (#14858) #15558 states in run-dev.js exactly why the listener is there and why it is not narrowed to EPIPE. run.js says nothing about the question.
  3. ⚠️ run.js is the file a customer's install actually runs. packages/cli's files is ['dist','README.md','CHANGELOG.md'] and its bin names only ./bin/run.js — so run.js ships (npm packs a bin target regardless of files, the The PUBLISHED CLI puts its own stdout/stderr on the blocking write path every time it spawns a child with inherited stdio #14874 finding) and run-dev.js does not. The guarded entry is the unpublished one.

What would settle it

  • Not "add the listener to be safe" — that is the shape this repo keeps clearing, a change with no measured question behind it. Establish first whether the class is reachable there at all.
  • The candidate that the two probes did not try: a payload large enough, or a lifecycle slow enough, that the process is still alive making stderr writes well after the first failure. os dev dies of an uncaught write EPIPE (exit 1) when its stderr read end is CLOSED — every other reader gets exit 2, and the drain is never reached #14858's own numbers say the dev shim crashed 938-1174 ms in, before run() settles; run.js's two probes both finished in well under a second at 57 and 35523 bytes.
  • If it is genuinely unreachable, that is a result: record why (which of the two entries' lifecycles differ, and where the published one's first stderr write sits relative to a settled run()), and this card closes on measurement.

Verified by this seat

  • packages/cli/package.json read at origin/main: files: ['dist','README.md','CHANGELOG.md'], bin: {objectstack: './bin/run.js', os: './bin/run.js'}. ⇒ the ships / does-not-ship split above is measured, not assumed.
  • Not deduped — no dedup search was run for this card.
  • ⛔ The two probe results are the dev's measurement, reported here as such; this seat did not re-run them.

Refs: #14858 · PR #15558 · #14874 (the bin-ships-regardless-of-files finding) · #14715 (which pinned exit 2) · #14832.

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

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions