Skip to content

[finding] os lint --json --eval --generator UNRESOLVABLE_PATH leaks esbuild's diagnostic to stderr — the sibling pin one file over asserts the opposite property holds #16358

Description

@os-litant

The defect

os lint --json --eval --generator UNRESOLVABLE_PATH writes 54 bytes to stderr — esbuild's own diagnostic, [ERROR] Could not resolve ... — while the run's --json face is otherwise well-formed on stdout.

A --json invocation is a machine face. Anything on stderr is a human-channel emission the caller did not ask for, and here it is an internal bundler's diagnostic, not an ObjectStack refusal.

⭐ Why this is a contradiction and not just a leak

One file over, packages/cli/test/lint-eval-generator-load-envelope.e2e.test.ts pins the opposite property:

expect(run.stderr).toBe('')

with the comment "A --json run leaks nothing to the human channel".

That pin is green, and it is honest about what it measures: it drives a module that exists and throws at import. The unresolvable-path branch is a different door into the same failure, it is uncovered, and it leaks.

Same command, same face, two answers — with a pin asserting the one that holds. The pin's comment states a property of the --json face as a whole; the code only delivers it on one of the two branches that reach the same catch.

Where it comes from

The leak is esbuild's logger writing directly, inside the bundleRequire call that loads the generator. The [ERROR] Could not resolve text is emitted during bundling, before any ObjectStack code sees a throw — so the surrounding try/catch, which does produce a correct one-key {error} JSON document on stdout, never gets the chance to suppress it.

Scope note — why it was not repaired where it was found

Measured during #16161's round, and deliberately not fixed in PR #16341. The repair is silencing esbuild's logger inside the bundleRequire call, and the correct form of that (a logLevel, a custom logger, or a captured stderr) is not settled by existing evidence in this repo — so it fails the bounded-in-place exemption and would have been an unargued rider on an unrelated diff.

Provenance and what a taker must re-run

⚠️ Measured by the implementer at branch point 4a1a3b0c254 / head 26bc91fc8e7. Not independently re-driven by the filing seat — recorded as the implementer's reading. Whoever takes this should re-run rather than inherit:

  1. the 54-byte stderr on the unresolvable-path branch, under --json;
  2. that lint-eval-generator-load-envelope.e2e.test.ts is green at the same head while (1) holds — the two together are the finding.

Dedupe

Run with a known-hit control in the same session, because a bare zero from this channel is not evidence of absence.

⇒ No duplicate.

A channel property worth recording: the control "Failed to load generator message text quality wording" returned total_count: 0 even though that exact phrase appears in the body of cards this same search returns under other phrasings. A zero from one phrasing of this semantic search is not a zero from the area — only a control sharing the failing query's vocabulary can settle that.

Grading

⛔ Filed unlabelled. domain:* and type grading are triage's, not this seat's. The implementer's suggestion, relayed as a suggestion only: domain:cli, bug.

Held on #16161 (comment 5559999365) while the dedupe channel was down; filed now that a positive control fires in the same session.

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

    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