I want my PowerShell script to stop when any of the commands I run fail (like set -e
in bash). I'm using both Powershell commands (New-Object System.Net.WebClient
) and programs (.\setup.exe
).

- 344,408
- 85
- 689
- 683

- 9,851
- 7
- 40
- 60
-
1See also [Equivalent of bash `set -e` · Issue #3415 · PowerShell/PowerShell](https://github.com/PowerShell/PowerShell/issues/3415). – li ki Oct 01 '21 at 15:26
-
9In PowerShell [7.3.0-preview.1](https://github.com/PowerShell/PowerShell/releases/tag/v7.3.0-preview.1), set `$PSNativeCommandUseErrorActionPreference` to `$true` and `$ErrorActionPreference` to `'Stop'` can stop the script execution when native command error occurs. Test code: `& { $PSNativeCommandUseErrorActionPreference = $true; $ErrorActionPreference = 'Stop'; ping.exe bbb; ping.exe aaa }` . – li ki Jan 21 '22 at 16:44
12 Answers
$ErrorActionPreference = "Stop"
will get you part of the way there (i.e. this works great for cmdlets).
However for EXEs you're going to need to check $LastExitCode
yourself after every exe invocation and determine whether that failed or not. Unfortunately I don't think PowerShell can help here because on Windows, EXEs aren't terribly consistent on what constitutes a "success" or "failure" exit code. Most follow the UNIX standard of 0 indicating success but not all do. Check out the CheckLastExitCode function in this blog post. You might find it useful.

- 16,104
- 25
- 61
- 88

- 194,368
- 42
- 353
- 369
-
4Does `$ErrorActionPreference = "Stop"` work for well-behaved programs (that return 0 on success)? – Andres Riofrio Mar 30 '12 at 20:56
-
25No, it doesn't work at all for EXEs. It only works for PowerShell cmdlets which run in-process. It is kind of pain but you have to check $LastExitCode after every EXE invocation, check that against the expected exit code and if that test indicates failure, you have to throw to terminate execution of the script e.g. `throw "$exe failed with exit code $LastExitCode"` where $exe is just the path to the EXE. – Keith Hill Mar 30 '12 at 21:18
-
2Accepted because it includes info on how to make it work with external programs. – Andres Riofrio Apr 22 '12 at 22:23
-
1
-
4I put in a feature request about it here: https://connect.microsoft.com/PowerShell/feedback/details/751703/option-to-stop-script-if-command-line-exe-fails – Helephant Jun 29 '12 at 14:12
-
5note that psake has a commandlet called "exec" which can you can use to wrap calls to external programs with a check for LastExitCode and display an error (and stop, if desired) – enorl76 Nov 28 '12 at 20:01
-
If you should discover something is bad and want to stop your program (which is why I found this page), then you should know that "Exit" is the command you're looking for. – Ryan Shillington Nov 13 '15 at 20:28
-
I think I misunderstood something. For `Connect-IscsiTarget -NodeAddress $NodeAddress` where `$NodeAddress` is invalid the script execution does not stop even PS throws the error _Connect-IscsiTarget : The target name is not found or is marked as hidden from login._. What am I doing wrong? – Peter VARGA Feb 25 '18 at 12:18
-
but!: be **careful to maybe reset the `ErrorActionPreference` finally since it overwrites it globally!** `$oldEap = $ErrorActionPreference ; $ErrorActionPreference = "Stop" ; try { ... } finally { $ErrorActionPreference = $oldEap }` like here: https://stackoverflow.com/a/15546666/1915920 – Andreas Covidiot Sep 28 '18 at 18:24
-
Within a script, setting `$ErrorActionPreference` impacts only the rest of the script and doesn't override the `global session` value of this variable. Well, unless you set the variable like this: `$global:ErrorActionPreference = 'Stop'`. – Keith Hill Sep 29 '18 at 19:19
-
1@Keith Hill: but not if you source it (what I do a lot and maybe others) via `$ErrorActionPreference = "Continue" ; . ./subscript.ps1 ; echo "$ErrorActionPreference"` (and *subscript.ps1* will set it to `$ErrorActionPreference = "Stop"` it will output `stop`! So it still seems a good practice to me. – Andreas Covidiot Oct 01 '18 at 10:16
-
actually `$oldEap` from my previous comment can be bad if this variable is used in a parent/ancestor script that dot sourced the child/descendants. I am not sure if "$local:oldEap = ..." would help here (which I doubt). – Andreas Covidiot Oct 01 '18 at 10:26
-
1Many years later, there's now an [RFC](https://github.com/PowerShell/PowerShell-RFC/pull/88/files) for introducing abort-on-failure support for external programs too. – mklement0 Jul 21 '19 at 19:47
-
Got caught by this too :S To me this makes PS unusable for buildscripts in CI, that need to exit on whatever first error - I don't wanna have to remember to wrap every single external cmd in `exec`. Running away quickly to `bash -e` – pjoe Feb 12 '20 at 12:51
-
If you only use PS commands, the Parameter `erroraction` with value `stop` would suffice. – Timo Apr 28 '20 at 06:16
-
I think we should all take a moment to reflect on how basically the problem here is that PowerShell wasn't designed properly and now we have to do these workarounds – A X Jun 27 '20 at 17:21
-
Note that there are some buggy cmdlets that cause this to not work 100% of the time. See my answer below for the additional things you want to do (`Set-StrictMode -Version Latest` and `$PSDefaultParameterValues['*:ErrorAction']='Stop'`) – aggieNick02 Jul 02 '20 at 17:04
-
`The term '' is not recognized as the name of a cmdlet, function, script file, or operable program. Check the spelling of the name, or if a path was included, verify that the path is correct and try again.` Got this error after adding this. any idea about the error? – JustWe May 18 '21 at 00:51
-
@JustWe I suggest you look at the detailed error info to determine the line in your script that is failing. To see this, execute `$error[0] | fl * -force` right after the script has failed. – Keith Hill May 19 '21 at 05:23
You should be able to accomplish this by using the statement $ErrorActionPreference = "Stop"
at the beginning of your scripts.
The default setting of $ErrorActionPreference
is Continue
, which is why you are seeing your scripts keep going after errors occur.

- 11,491
- 7
- 53
- 69
-
33
-
This is an incomplete answer, however it was posted prior to the currently selected answer which builds on it. So it deserve its upvotes too, imho. – mins Nov 10 '22 at 14:30
-
Sadly, due to buggy cmdlets like New-RegKey and Clear-Disk, none of these answers are enough. I've currently settled on the following code in a file called ps_support.ps1
:
Set-StrictMode -Version Latest
$ErrorActionPreference = "Stop"
$PSDefaultParameterValues['*:ErrorAction']='Stop'
function ThrowOnNativeFailure {
if (-not $?)
{
throw 'Native Failure'
}
}
Then in any powershell file, after the CmdletBinding
and Param
for the file (if present), I have the following:
$ErrorActionPreference = "Stop"
. "$PSScriptRoot\ps_support.ps1"
The duplicated ErrorActionPreference = "Stop"
line is intentional. If I've goofed and somehow gotten the path to ps_support.ps1
wrong, that needs to not silently fail!
I keep ps_support.ps1
in a common location for my repo/workspace, so the path to it for the dot-sourcing may change depending on where the current .ps1
file is.
Any native call gets this treatment:
native_call.exe
ThrowOnNativeFailure
Having that file to dot-source has helped me maintain my sanity while writing powershell scripts. :-)

- 2,557
- 2
- 23
- 36
A slight modification to the answer from @alastairtree:
function Invoke-Call {
param (
[scriptblock]$ScriptBlock,
[string]$ErrorAction = $ErrorActionPreference
)
& @ScriptBlock
if (($lastexitcode -ne 0) -and $ErrorAction -eq "Stop") {
exit $lastexitcode
}
}
Invoke-Call -ScriptBlock { dotnet build . } -ErrorAction Stop
The key differences here are:
- it uses the Verb-Noun (mimicing
Invoke-Command
) - implies that it uses the call operator under the covers
- mimics
-ErrorAction
behavior from built in cmdlets - exits with same exit code rather than throwing exception with new message

- 14,227
- 9
- 74
- 124
-
1How do you pass parameters / variables? e.g. `Invoke-Call { dotnet build $something } ` – Michael Blake May 24 '19 at 08:42
-
1@MichaelBlake the inquiry of yours is so right, allowing a params passthrough would make this approach gold. I am inspecting https://adamtheautomator.com/pass-hashtables-invoke-command-argument/ to adjust the Invoke-Call to support params pass-through. If I succeed, I will post it as another answer here. – Maxim V. Pavlov Sep 07 '19 at 17:35
-
1Why do you use the splatting operator during the invoke? What does that get you? `& @ScriptBlock` and `& $ScriptBlock` appear to do the same thing. Haven't been able to google what the difference is in this case – pinkfloydx33 Oct 12 '19 at 14:03
You need slightly different error handling for powershell functions and for calling exe's, and you need to be sure to tell the caller of your script that it has failed. Building on top of Exec
from the library Psake, a script that has the structure below will stop on all errors, and is usable as a base template for most scripts.
Set-StrictMode -Version latest
$ErrorActionPreference = "Stop"
# Taken from psake https://github.com/psake/psake
<#
.SYNOPSIS
This is a helper function that runs a scriptblock and checks the PS variable $lastexitcode
to see if an error occcured. If an error is detected then an exception is thrown.
This function allows you to run command-line programs without having to
explicitly check the $lastexitcode variable.
.EXAMPLE
exec { svn info $repository_trunk } "Error executing SVN. Please verify SVN command-line client is installed"
#>
function Exec
{
[CmdletBinding()]
param(
[Parameter(Position=0,Mandatory=1)][scriptblock]$cmd,
[Parameter(Position=1,Mandatory=0)][string]$errorMessage = ("Error executing command {0}" -f $cmd)
)
& $cmd
if ($lastexitcode -ne 0) {
throw ("Exec: " + $errorMessage)
}
}
Try {
# Put all your stuff inside here!
# powershell functions called as normal and try..catch reports errors
New-Object System.Net.WebClient
# call exe's and check their exit code using Exec
Exec { setup.exe }
} Catch {
# tell the caller it has all gone wrong
$host.SetShouldExit(-1)
throw
}

- 3,960
- 32
- 49
-
Invoking eg: `Exec { sqlite3.exe -bail some.db "$SQL" }`, the `-bail` causes an error since it's trying to interpret it as a Cmdlet parameter? Wrapping things in quotes doesn't seem to work. Any ideas? – rcoup Mar 25 '20 at 11:19
-
Is there a way to put this code somewhere central so you can do some kind of #include when you want to use the Exec method? – NickG May 21 '20 at 12:46
-
Yeah, you can. You can put it a file named `powershell-error-handling-sanity.ps1` and then dot-source the ps1 file at t he top of any other ps1 file with `.
– aggieNick02 Jul 02 '20 at 17:14
I'm new to powershell but this seems to be most effective:
doSomething -arg myArg
if (-not $?) {throw "Failed to doSomething"}

- 2,921
- 1
- 29
- 31
for people coming here on 2021 this is my solution that covers both cmdlets and programs
function CheckLastExitCode {
param ([int[]]$SuccessCodes = @(0))
if (!$?) {
Write-Host "Last CMD failed $LastExitCode" -ForegroundColor Red
#GoToWrapperDirectory in my code I go back to the original directory that launched the script
exit
}
if ($SuccessCodes -notcontains $LastExitCode) {
Write-Host "EXE RETURNED EXIT CODE $LastExitCode" -ForegroundColor Red
#GoToWrapperDirectory in my code I go back to the original directory that launched the script
exit
}
}
you can use it like this
cd NonExistingpath
CheckLastExitCode

- 2,550
- 2
- 18
- 36
As far as I know, Powershell does not have any automatic handling of non-zero exit codes returned by sub-programs it invokes.
The only solution I know about so far to mimick the behavior of bash -e
is to add this check after every call to an external command:
if(!$?) { Exit $LASTEXITCODE }

- 935
- 8
- 13
-
I like this solution, it's just one line to add after each external program call. – bounav Dec 23 '22 at 13:39
-
@bounav: Well, this is relative. Let's just say it's the "less worse" solution I've found. On the other hand in a bash script you just add `#!/bin/bash -e` at the beginning of each script and it applies to the whole script all the time. I really really hate Powershell just for that stupid error handling. (But unfortunately I don't have much choice using it sometimes...) – nicolas-van Apr 22 '23 at 07:46
I came here looking for the same thing. $ErrorActionPreference="Stop" kills my shell immediately when I'd rather see the error message (pause) before it terminates. Falling back on my batch sensibilities:
IF %ERRORLEVEL% NEQ 0 pause & GOTO EOF
I found that this works pretty much the same for my particular ps1 script:
Import-PSSession $Session
If ($? -ne "True") {Pause; Exit}

- 76
- 1
- 4
Seems like simple rethrow does the trick.
param ([string] $Path, [string] $Find, [string] $Replace)
try {
((Get-Content -path $Path -Raw) -replace $Find, $Replace) | Set-Content -Path $Path
Write-Output Completed.
} catch {
# Without try/catch block errors don't interrupt program flow.
throw
}
Now output Completed appears only after successful execution.

- 7,456
- 3
- 46
- 66
Redirecting stderr
to stdout
seems to also do the trick without any other commands/scriptblock wrappers although I can't find an explanation why it works that way..
# test.ps1
$ErrorActionPreference = "Stop"
aws s3 ls s3://xxx
echo "==> pass"
aws s3 ls s3://xxx 2>&1
echo "shouldn't be here"
This will output the following as expected (the command aws s3 ...
returns $LASTEXITCODE = 255
)
PS> .\test.ps1
An error occurred (AccessDenied) when calling the ListObjectsV2 operation: Access Denied
==> pass

- 4,041
- 3
- 33
- 50
I recommend to apply simple exit code validation. Depends on how sophisticated solution you need.
Best for single validation - stops only when docker-compose fails:
docker-compose up --detach || exit 1
Good if you want to stop the script and propagate error message
docker-compose up --detach || throw "Script has failed, check logs above"
or more complex and reusable function:
function terminate() {
Write-Error "Found exit code: $LASTEXITCODE"
throw "Last executed operation has failed, check logs above!"
}
docker-compose up --detach || terminate

- 437
- 5
- 11