Skip to content

fix(interpreter): route builtin, command-not-found and $(...) stderr like bash - #2634

Merged
chaliy merged 2 commits into
mainfrom
claude/stderr-redirect-fidelity
Oct 8, 2026
Merged

chaliy merged 2 commits into
mainfrom
claude/stderr-redirect-fidelity

Conversation

@chaliy

@chaliy chaliy commented Oct 8, 2026

Copy link
Copy Markdown
Contributor

Requested by Михайло · project thread

What changed

Error output now goes where bash sends it.

  • A command's own failure honors its redirects: nocmd 2>/dev/null, ./missing 2>/dev/null, return 2>&1, and the usage errors of source/./getopts/builtin.
  • Stderr written inside $(...) reaches the outer stderr (it was dropped), ahead of the expanding command's own stderr; $(< missing) reports the missing file.
  • eval keeps the caller's $LINENO and diagnostic line.
  • exit with no argument uses $?; exit abc reports numeric argument required and exits 2; exit 1 2 reports too many arguments.
  • /proc refuses new files like procfs (No such file or directory), unless the embedder's own filesystem provides that directory. mkdir/touch print plain error text instead of io error: ....

Why

Agents read stderr to see what failed. A lost $(nocmd) error hides the cause; a 2>/dev/null that does not silence "command not found" pollutes output the script meant to suppress; a write to /proc/x that succeeds hides a mistake a real OS would report.

Before / After

Same script through bashkit (after) and bash 5.2:

$ cat c.sh
x=$(nocmd)
nocmd 2>/dev/null
echo rc=$?
eval 'echo $LINENO'
echo y > /proc/nope; echo rc=$?

$ bashkit c.sh            # identical to `bash c.sh`
c.sh: line 1: nocmd: command not found
rc=127
4
c.sh: line 5: /proc/nope: No such file or directory
rc=1

Before: line 1's error was missing, line 2 printed command not found, eval printed 1, and the /proc write succeeded with rc=0.

bash-oracle (106 cases): match 98 → 101, strict 89 → 94; floors raised.

Risk

Stderr from command substitutions is new output; it is capped at max_stderr_bytes and held per interpreter, so tenants stay isolated. exit abc now exits 2 instead of 0. Remaining gaps are in knowledge/operations/limitations.md ("Stderr routing"): { a; b; } 2>&1 orders merged stderr after the group's stdout; multi-line eval line counting.

Checklist

  • Failing tests first: tests/integration/stderr_fidelity_tests.rs (8), spec_cases/bash/stderr-redirect-fidelity.test.sh (18, compared with real bash), rootfs/flow unit tests
  • fmt, clippy -D warnings, integration (1447), lib (2940), bashkit-cli, bash_comparison --ignored
  • Knowledge: log.md, limitations.md, foundations/vfs.md

Generated by Claude Code

…like bash

- A command's own failure honors its redirects: nocmd 2>/dev/null, ./missing 2>/dev/null, return 2>&1 and source/./getopts/builtin usage errors.
- Stderr written inside $(...) reaches the outer stderr, ahead of the expanding command's own and outside its redirects; $(< missing) reports.
- eval counts $LINENO and diagnostics from the eval's own line.
- exit uses $? with no argument, rejects non-numeric arguments (status 2) and treats extra arguments as a line-discarding error.
- /proc refuses new entries like procfs (ENOENT); mkdir/touch print the strerror text.

bash-oracle: match 98 -> 101, strict 89 -> 94 (with main).
@chaliy chaliy self-assigned this Oct 8, 2026
@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Oct 8, 2026 •

Copy link
Copy Markdown

Deploying with  Cloudflare Workers  Cloudflare Workers

The latest updates on your project. Learn more about integrating Git with Workers.

Status Name Latest Commit Preview URL Updated (UTC)
✅ Deployment successful!
View logs
bashkit 8e57fca Commit Preview URL

Branch Preview URL
Oct 08 2026, 06:36 AM

… use

The coverage build overflowed depth_32_no_stack_overflow. The new await
for `"" 2>/dev/null` in execute_simple_command grew its frame; the null
command now makes one boxed await for both cases. Held `$(...)` stderr
lives on the interpreter instead of in async locals, and own-error
redirects go through the boxed redirect_result.

Debug stack for the test: main 2.007 MB, before 2.056 MB, now 1.903 MB.

chaliy commented Oct 8, 2026

Copy link
Copy Markdown
Contributor Author

Generate Rust Coverage failed on 324d618: stack_overflow_regression_tests::depth_32_no_stack_overflow overflowed its 2 MB test-thread stack under tarpaulin. It is this PR's: a new await in execute_simple_command (for "" 2>/dev/null) grew the frame on the $(...) recursion path. Fixed in 8e57fca by folding it into one boxed await and moving held substitution stderr off the async frames. Measured minimal debug stack for the test: main 2.007 MB, PR before 2.056 MB, PR now 1.903 MB.


Generated by Claude Code

@chaliy
chaliy merged commit 31894e8 into main Oct 8, 2026
46 checks passed
@chaliy
chaliy deleted the claude/stderr-redirect-fidelity branch October 8, 2026 06:54
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant