shell_snapshot: PowerShell state+environment capture always returns None
Nobody has claimed this yet.
- Dominant language
- Rust
- Stars
- 125k
- Forks
- 19.4k
- PR merge metrics
- PR metrics pending
Description
Edit (2026-08-29): Updated below per @HuzaifaChaudary's comment — the root cause (missing marker) is confirmed, but my original "expected behavior" and suggested fix were wrong. Adding the marker back would make
snapshot_state_and_environment_scriptreturnSome(script)for PowerShell, but the restore side (exec-server/src/shell_snapshot.rs) can't consume a PowerShell script — it hitsShellType::PowerShell | ShellType::Cmd => unreachable!(). Today'sNoneis safe, just accidentally so (nothing currently calls this with PowerShell). The corrected fix makes that refusal explicit instead of incidental. Original report kept below for context; corrected sections are marked.
What issue are you seeing?
snapshot_state_and_environment_script(ShellType::PowerShell) always returns None. This contradicts (Correction: see below — snapshot_script's contract, which only documents Cmd as unsupported.None here is actually the right outcome today, just for a different/more fragile reason than intended.)
// codex-rs/shell-command/src/shell_snapshot.rs
const EXPORT_CAPTURE_MARKER: &str = "# Capture exported variables";
pub fn snapshot_state_and_environment_script(shell_type: ShellType) -> Option<String> {
let script = snapshot_script(shell_type)?;
let (state, _) = script.split_once(EXPORT_CAPTURE_MARKER)?;
Some(format!("{state}printf '\\0'\n/usr/bin/env -0\n"))
}
snapshot_state_and_environment_script locates the exports section by splitting on the literal comment "# Capture exported variables". That comment is present verbatim in the Zsh, Bash, and Sh snapshot templates (lines 60, 112, 167), but was never added to powershell_snapshot_script() (lines 202–225):
$ grep -n "Capture exported variables" codex-rs/shell-command/src/shell_snapshot.rs
8:const EXPORT_CAPTURE_MARKER: &str = "# Capture exported variables";
60:# Capture exported variables <- zsh_snapshot_script
112:# Capture exported variables <- bash_snapshot_script
167:# Capture exported variables <- sh_snapshot_script
Zero matches inside powershell_snapshot_script(). So for ShellType::PowerShell, script.split_once(EXPORT_CAPTURE_MARKER) returns None, and the ? propagates it — the function unconditionally returns None for PowerShell.
What steps can reproduce the bug?
Provable by text inspection of codex-rs/shell-command/src/shell_snapshot.rs — no build required:
- Note
EXPORT_CAPTURE_MARKERat line 8. - Note it appears at lines 60 (zsh), 112 (bash), 167 (sh).
- Note
powershell_snapshot_script()(lines 202–225) never contains that string. - Therefore
snapshot_state_and_environment_script(ShellType::PowerShell)always evaluates toNone.
(Added per the correction): the environment half of the script this function builds is POSIX-only (printf '\0' + /usr/bin/env -0), and the restore side builds a POSIX eval to consume it:
// exec-server/src/shell_snapshot.rs
let (shell_flag, startup) = match shell_type {
ShellType::Bash => ("-pc", "set +o privileged\n"),
ShellType::Zsh => ("-fc", "setopt RCS\n"),
ShellType::Sh => ("-c", ""),
ShellType::PowerShell | ShellType::Cmd => unreachable!(),
};
So a PowerShell script coming out of this function today could be neither produced nor restored end-to-end. Nothing currently calls it with PowerShell: one consumer gates on shell name before ever reaching it, and a separate path already refuses explicitly (core/src/shell_snapshot.rs: bail!("Shell snapshot not supported yet for {shell_type:?}")). Today's None is safe only because of that outer gate.
What is the expected behavior?
snapshot_state_and_environment_script(ShellType::PowerShell) should return Some(script)
Corrected: it should keep returning None for PowerShell/Cmd — but explicitly, because that shell isn't supported, not incidentally because a template is missing a comment. As written today, the None is safe by accident: anything that widens the gate around this function later (without knowing about the marker dependency) inherits a latent bug.
Additional information
Root cause: the export-capture marker comment was added to the three POSIX shell templates but never ported to the PowerShell template — this part of the original report was correct.
Suggested fix (superseded, do not use) — my original suggestion was to add the marker comment to powershell_snapshot_script(). Per the correction, this is wrong: it would make the function return Some(script) for PowerShell, handing exec-server a script its restore path answers with unreachable!().
Corrected suggested fix (credit: @HuzaifaChaudary) — make the refusal explicit and shell-based instead of incidental to template contents:
pub fn snapshot_state_and_environment_script(shell_type: ShellType) -> Option<String> {
match shell_type {
ShellType::Zsh | ShellType::Bash | ShellType::Sh => {}
ShellType::PowerShell | ShellType::Cmd => return None,
}
let script = snapshot_script(shell_type)?;
let (state, _) = script.split_once(EXPORT_CAPTURE_MARKER)?;
Some(format!("{state}printf '\\0'\n/usr/bin/env -0\n"))
}
None before and after this change — no behavior change today, but the reason becomes legible instead of coincidental. snapshot_script's doc comment (which names only Cmd as unsupported) is also worth revisiting, since that's what made the original state read as an oversight rather than a deliberate gate.
Per their comment, they have this written with two tests passing on a fork branch (cargo test -p codex-shell-command, 130 passed, fmt/clippy clean) that fail if the marker is added on its own instead — consistent with the correction above. They couldn't open a PR since this repo doesn't accept them from forks.
If PowerShell snapshot support is actually wanted, that's a separate, larger change (a PowerShell state script plus an environment format the restore side can read) — this issue is only about the current None not reading as a one-line oversight when it isn't one.
Found via static code review (commit 7c3747941; correction via community review), not via a live repro.
Possibly related: #25833 and #28408 both show the runtime symptom this code path produces ("Failed to create shell snapshot for powershell: Shell snapshot not supported yet for PowerShell").
Contributor guide
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start in codex-rs/shell-command/src/shell_snapshot.rs, then read the restore match in exec-server/src/shell_snapshot.rs and the related refusal in core/src/shell_snapshot.rs. Run cargo test -p codex-shell-command before and after the change. Done means PowerShell and Cmd remain explicitly unsupported, POSIX snapshot behavior is unchanged, and formatting and clippy remain clean.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- rust
- Domain
- cli
- Issue type
- Bug
- Difficulty
- 3/5
- Estimated time
- 1-2 days
- Activity status
- Active
- Clarity
- Clearly specified
- Newbie friendliness
- 64/100