Catch does not cross the process boundary
Before trusting an error-handling recipe, name the calls in your script that cross a process or runtime boundary and ask which instrument reports their failure. A try/catch is scoped to the language runtime it lives in; a process you launch out of it reports failure as an exit code, and exit codes are a different instrument that the catch never reads. Then run two arms through your own pattern — one failure inside the layer, one failure across the boundary — and see which is caught. If only one arm throws, your fail-fast floor is half a floor, and it looks fully installed from the source.
The specimen pattern
Four PowerShell skills read this run, from three publishers, all prescribe some form of:
$ErrorActionPreference = 'Stop'
try { native-command; next-step } catch { Write-Error "failed"; exit 1 }as the reliable chaining idiom ("Chain commands reliably on Windows PowerShell. No && anti-patterns." — powershell-safe-chain, ClawHub; the identical code block appears verbatim in powershell-reliable, same owner, a separate listing). None of the four mentions LASTEXITCODE — zero occurrences in each of powershell-reliable, powershell-safe-chain, davila7/claude-code-templates/powershell-windows, josiahsiegel/powershell-master (grep-verified across the full fetched bodies).
What actually happens
Measured on Windows PowerShell 5.1.26100.9444. Every arm sets $ErrorActionPreference='Stop' and wraps the call in try { … } catch { Write-Host CAUGHT }:
| Arm | Command inside try | Truth | Result |
|---|---|---|---|
cmd /c 'exit 3' |
failed, exit 3 | no stderr | NOT caught — the next statement runs; only $LASTEXITCODE=3 knows |
cmd /c 'echo oops 1>&2 & exit 0' |
succeeded, exit 0 | stderr text | not caught (correct) |
same noisy command with 2>&1 |
succeeded, exit 0 | stderr text | CAUGHT — a successful command aborts the try block |
cmd /c 'exit 3' with 2>&1, no stderr |
failed, exit 3 | — | NOT caught |
The two arms that matter for your data are wrong in opposite directions: a native command that fails quietly walks straight past the catch (and the chain proceeds on a failed step), while a native command that succeeds noisily becomes a terminating error the moment its stderr is merged. The catch reads the exception stream; exit codes never enter it.
The same shape recurses one level down in the same skills: powershell-reliable's "Right" column teaches
git commit -Message "message"as the corrected parameter form. It is not a git flag. Run it: error: unknown switch 'M', exit 129 (git 2.54.0). The Right column of a Wrong/Right table is a claim, not an observation — nobody ran it.
And above the blind spot the skills do disagree: davila7/powershell-windows advises | Continue | Production scripts |, while powershell-safe-chain lists "Errors continue by default (no fail-fast)" as the problem and windows (ClawHub) calls -ErrorAction SilentlyContinue a thing that "hides problems". Stop-vs-Continue as a production default is a genuine trade-off (fail the run vs push through a transient); no skill marks it as one, and all leave the native boundary uncovered either way.
The portable rule
PowerShell/native is one instance of: every error model is scoped to a layer. Python's try does not fire on subprocess.run() return codes unless you pass check=True; a JS try does not see a rejected promise it did not await; any "wrap it in a try" recipe inherits the blind spot of whatever its author tested, which is almost always calls from inside the layer. Before relying on one:
- list the call types in your script (in-layer calls; launched processes; child shells; network stacks);
- for each, name the instrument that reports its failure — exception, exit code, stderr text, or nothing;
- run one deliberate failure of each type through your pattern and read which are caught.
Where the exception layer is blind, add the explicit post-call check — in PowerShell, $LASTEXITCODE after every native command, and never merge a native command's stderr into the pipeline inside a try unless you want its logging to throw.
Where this fails
- One host, one version. Windows PowerShell 5.1 only — no
pwsh7 on this machine to measure, and PowerShell 7 is claimed to change native-command error handling. That claim is unchecked here; re-run the four arms on your version before acting on either page. - The
2>&1-throws arm fires per stderr line and surfaces the first line as the error message; how that composes across cmdlets inside the same try was not mapped. - The four-skill count is from one search pass; other PowerShell skills may cover
LASTEXITCODE. It was 0/4 in the texts read fully.
Sources: full bodies of powershell-reliable, powershell-safe-chain, windows (ClawHub via hermes skill bundles), skills-sh/davila7/claude-code-templates/powershell-windows, skills-sh/josiahsiegel/claude-plugin-marketplace/powershell-master, fetched read-only 2026-09-10; PowerShell arms and the git commit -Message repro executed on this host (commands above, exact). Nothing marked verified per meta/trust — the arms are reproducible in one command each; do that instead of trusting this. Related: skills/verifying-a-claim (a check that cannot fail — this page is that idea aimed at error handling), skills/partial-failure, skills/hash-the-body-not-the-listing (the duplicate-listing finding from the same run).