PowerShell / Intermediate: binding, errors, modules, data, and tests / Lesson 8
Handle errors and native exit status deliberately
Separate terminating exceptions, non-terminating errors, preferences, catch behavior, and native-process results without treating a clean console as success.
On this page
Lesson goals
- Explain when try/catch handles an error.
- Use ErrorAction and preferences at reviewed scopes.
- Preserve native exit codes and original failure evidence.
PowerShell has more than one failure behavior
Some errors terminate a statement or unwind execution, while non-terminating errors can be reported and allow processing to continue. try/catch handles terminating errors under its documented rules; it does not automatically catch every message written to the error stream. A command can emit partial success objects and then fail, so visible data does not prove completion. Classify the error, affected input, and whether continued processing is acceptable. Do not silence all errors to make a report clean. A useful failure contract states what can be partial and which results must be rejected.
ErrorAction is a policy input, not rollback
A supported common parameter such as -ErrorAction Stop can promote applicable non-terminating errors into a terminating path. Preference variables influence behavior more broadly and should be scoped or restored in reusable code. Ignore and SilentlyContinue have different reporting/history implications and neither makes the operation correct. Strict mode can expose selected programming mistakes but is not an input schema or security boundary. Read the selected command’s contract, preserve the original ErrorRecord context, and avoid replacing it with a generic success message after catch. Recovery still needs explicit postconditions and ownership of effects.
Catch the intended exception and keep cleanup separate
Typed catch clauses can distinguish documented exception types, while a final general catch may provide an appropriate boundary response. finally supports cleanup on the language’s relevant exit paths, but does not undo an already committed external action or survive every machine failure. Do not catch everything and return an empty collection when the caller needs to know the operation failed. The following synthetic example throws and handles its own argument exception only; it does not open files or call a host command. It remains illustrative because no PowerShell engine executed it.
try {
throw [System.ArgumentException]::new('synthetic input')
}
catch [System.ArgumentException] {
'handled argument contract'
}
finally {
$cleanupState = 'complete'
}
The intended message is handled argument contract. The example is non-runnable and does not establish actual ErrorRecord or cleanup behavior under any installed edition.
Native exit codes are a separate interface
A native executable supplies its own exit code and stdout/stderr semantics. $LASTEXITCODE and $? answer different questions and should be captured at the relevant boundary before later work changes context. Newer PowerShell versions include documented native-error preference behavior that Windows PowerShell 5.1 does not share; do not assume every nonzero native exit automatically becomes a catchable exception. Some tools use nonzero for an expected condition. Decide interpretation from the tool’s documentation and propagate a truthful script result. No native process or administrative command is executed by this lesson.
Test outcome policy without invoking the failing operation
An offline result fixture can show required failure versus expected absence. It does not implement PowerShell error promotion, ErrorRecord types, native preference variables, or exception handling. Keep the original code and category in the model, then derive the public decision from a documented policy. Later engine tests should cover partial output, terminating and non-terminating errors, cleanup failure, and native exit interpretation with harmless local programs. A successful catch path alone is insufficient if it leaves the application in an unacceptable state or reports completed work that never happened.
outcomes = [{'name': 'required', 'code': 7, 'allowed': {0}}, {'name': 'optional-search', 'code': 1, 'allowed': {0, 1}}]
for outcome in outcomes:
print(outcome['name'] + '=' + ('accepted' if outcome['code'] in outcome['allowed'] else 'failed'))
required=failed
optional-search=accepted
The numbers and allowed sets belong to this synthetic policy only. No failing native executable, PowerShell command, or host resource is invoked.
Common errors and fixes
try/catch is expected to catch every error message
- Why it happens
- Error-stream output and terminating exceptions were conflated.
- How to fix it
- Review error category and supported ErrorAction behavior.
- Prevent it next time
- Test both terminating and non-terminating paths.
A catch returns empty data and hides failure
- Why it happens
- Recovery output violated the caller’s completion contract.
- How to fix it
- Return or throw a truthful documented outcome.
- Prevent it next time
- Assert failure status as well as data.
A native nonzero exit is assumed catchable everywhere
- Why it happens
- Edition and native-error policy differences were ignored.
- How to fix it
- Capture and interpret the native exit contract explicitly.
- Prevent it next time
- Declare tested native invocation behavior per edition.
Check your understanding
1. Why can partial success objects coexist with a failed operation?
Show worked solution
Earlier records may have been emitted before a later error. The caller must know whether those records are acceptable or must be discarded. Complete output and truthful failure reporting should be tested together rather than inferring success from nonempty data.
2. What must remain separate from successful exception handling?
Show worked solution
Actual recovery and application postconditions remain separate. Catching an error does not undo a file or remote transaction, establish health, or prove that retrying the operation is safe. Verify those requirements explicitly.
Key takeaways
- Classify errors and preserve the original outcome.
- Keep native status, exception policy, and effect recovery distinct.