Skip to content

subshell and brace-group bodies do not update $? between commands (always reads the pre-block value) #476

Description

@zechengz

Bug

Inside ( ... ) and { ... ; }, $? does not reflect the previous command in the block. It keeps whatever value it had before the block started, so error-checking patterns silently read the wrong exit code.

Repro (RAM mount, both languages)

$ (false; echo subshell=$?)
mirage: subshell=0
bash:    subshell=1

$ { false; echo group=$?; }
mirage: group=0
bash:    group=1

Top-level sequences are fine (false; echo $? prints 1): list connections seed session.last_exit_code between commands (python workspace/executor/pipes.py does this in handle_connection at three sites). The subshell body loop (handle_subshell, same file) and the brace-group loop (the COMPOUND branch of workspace/node/execute_node.py) iterate children with execute_node but never assign session.last_exit_code = io.exit_code after each child, so $? expansion (workspace/expand/variable.py reads session.last_exit_code) sees the stale value. TypeScript mirrors the same structure (handleSubshell in workspace/executor/pipes.ts, COMPOUND branch of workspace/node/execute_node.ts) and has the same bug.

Fix shape: set last_exit_code after each child in both loops (and restore the saved value on subshell exit alongside the existing env/cwd restore, since bash subshells do not leak $? changes... actually bash subshells DO propagate their final exit status to the parent's $?, so only the in-loop seeding needs care). Found while writing arithmetic-command integ cases (the cases were rewritten to avoid depending on this).

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingcommandsshell command coverage and semantics

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions