In PowerShell, what is the difference between throw and Write-Error, and when would you choose each inside a function?
answer
- one halts, one reports
- error stream versus unwinding
- $? and $Error are set either way
- reaches the nearest catch
basics
~10 sthrow raises a terminating error that unwinds to the nearest catch and stops the current scope. Write-Error emits a non-terminating error record on the error stream and execution continues with the next statement.
solid answer
~40 s`throw` raises a **terminating** error: the pipeline stops and control unwinds to the nearest enclosing `catch`, or the script aborts if there is none. `Write-Error` raises a **non-terminating** error: it writes an `ErrorRecord` to the error stream, the host prints it, `$?` becomes `$false`, the record is appended to `$Error` — and the very next line of your function still runs. So I use `Write-Error` when a single item failed but the work is still worth continuing, which lets the caller decide with `-ErrorAction` whether to stop; and I use `throw` when the function is in a state it cannot proceed from — a missing mandatory resource, a failed precondition. A common bug is calling `Write-Error` and assuming the function stopped there; if you want to bail, you have to `return` or `throw`.
go deeper
Be able to say plainly that throw stops the current scope while Write-Error reports a problem and lets the next statement run, and show one line of each.
Explain that Write-Error emits an ErrorRecord on the error stream and sets $? and $Error, whereas throw unwinds the pipeline to the nearest catch or aborts the script.
Show the decision rule: per-item recoverable failures as non-terminating so the caller keeps control via -ErrorAction, unrecoverable state as a throw, and mention supplying a target object and error ID.
Own the convention across a shared codebase: which failures callers are expected to survive, whether every function is safe to run under $ErrorActionPreference = 'Stop', and how that shapes retry logic downstream.
## Two ways for your code to say "that failed" PowerShell distinguishes terminating from non-terminating errors, and as the *author* of a function you choose which one you emit. `throw` and `Write-Error` are the two doors. ## throw `throw` raises a terminating error. Execution of the current pipeline stops immediately and PowerShell unwinds until it finds a `catch` block; if none exists, the error reaches the top and the script ends. ```powershell function Get-Config { param([string]$Path) if (-not (Test-Path $Path)) { throw "Config file not found: $Path" } Get-Content $Path } ``` Throwing a bare string is idiomatic and readable, but note what the caller receives: PowerShell wraps a thrown string in a `System.Management.Automation.RuntimeException`, so a caller cannot filter it with a meaningful typed `catch`. If callers need to distinguish your failure from every other failure, throw a real exception instead: ```powershell throw [System.IO.FileNotFoundException]::new("Config file not found: $Path") ``` ## Write-Error `Write-Error` constructs an `ErrorRecord` and writes it to the error stream. It is a *report*, not a halt: ```powershell function Test-Thing { Write-Error 'something went wrong' 'this line still runs' } ``` Calling `Test-Thing` prints the error **and** emits the string. Three side effects come with it: the automatic variable `$?` is set to `$false`, the record is prepended to `$Error`, and — crucially — the caller keeps control of what happens next. A caller who wants your reported failure to be fatal writes `Test-Thing -ErrorAction Stop`; a caller doing bulk work writes `-ErrorAction SilentlyContinue -ErrorVariable +failures` and carries on. `Write-Error` also accepts richer input than a message string: `-Message`, `-Category`, `-ErrorId`, `-TargetObject` and `-ErrorRecord` let you hand the caller something worth inspecting programmatically. `-TargetObject` in particular is how you tell the caller *which* item failed, which matters a lot in loops. ## Choosing between them The rule of thumb interviewers want to hear: - **Non-terminating (`Write-Error`)** when the failure is scoped to one item and the rest of the work is still meaningful — one unreachable host in a list of fifty, one file that could not be parsed. You report it and move on, and you leave the escalation decision to the caller. - **Terminating (`throw`)** when the function cannot sensibly continue — a mandatory parameter that fails validation, a missing dependency, an authentication failure that makes every subsequent call pointless. Continuing would only produce a cascade of secondary errors that obscure the real one. ## The classic bug ```powershell function Save-Report { param($Data) if (-not $Data) { Write-Error 'No data supplied' # reports... } $Data.Rows.Count # ...and then runs anyway } ``` The author expected `Write-Error` to act like `throw`. It does not: the function continues and fails again a line later with a confusing null-reference error that hides the real cause. If you report and want to stop, follow it with `return`; if the situation is genuinely unrecoverable, use `throw` in the first place. ## Related surfaces worth naming `Write-Warning` and `Write-Verbose` are *not* errors — they go to different streams, never set `$?`, and never populate `$Error`. Using `Write-Host` to announce a failure is worse still: it puts the message on the display where no caller can capture, filter or count it. And inside an advanced function, `$PSCmdlet.ThrowTerminatingError()` is the fully-featured version of `throw` — it emits an `ErrorRecord` you built yourself, with your own error ID and target object, rather than a wrapped string. ## What the caller sees Whichever you choose, everything the caller can inspect arrives as an `ErrorRecord`: `$_` inside their `catch`, or the entries in `$Error` and in an `-ErrorVariable`. Writing a good message, a real target object and a stable error ID is therefore not decoration — it is the interface your failure exposes.
- After Write-Error runs, how does a caller detect that something went wrong?Three ways. The automatic variable `$?` is `$false` immediately after the call; the `ErrorRecord` is added to `$Error`; and the caller can capture it directly with `-ErrorVariable`. A caller who wants it fatal instead passes `-ErrorAction Stop`, turning your non-terminating error into a catchable one.
- Why might throwing a bare string be a poor choice for a function other people call?PowerShell wraps a thrown string in a RuntimeException, so callers cannot write a meaningful typed catch to distinguish your failure from any other. Throwing a real exception type — or, in an advanced function, calling $PSCmdlet.ThrowTerminatingError with a constructed ErrorRecord — gives them something to filter on.
- Is Write-Warning an alternative to Write-Error for reporting a failure?No. Write-Warning goes to the warning stream: it does not set $? to $false, does not populate $Error, and cannot be escalated with -ErrorAction Stop. It is for advisory notes about work that succeeded, not for reporting work that failed.
saying these in an interview costs you the question
- Assumes Write-Error stops the function like return
- Calls Write-Host to announce failures
- Thinks throw and Write-Error are interchangeable
- Says Write-Warning reports an error
- Believes Write-Error sets a process exit code