12

I have a script that runs an external EXE file. When that EXE file fails (sets errorlevel to 1), the PowerShell script fails.

I'm running curl.exe and am getting this:

  • CategoryInfo : NotSpecified: ( % Total % ... Time Current:String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError

How can I ignore/catch the failure of the external EXE file and continue with my script?

js2010
  • 23,033
  • 6
  • 64
  • 66
ripper234
  • 222,824
  • 274
  • 634
  • 905
  • Are you sure it is an external EXE returning an error code that is causing PowerShell to error? That doesn't normally cause an error to be thrown within PowerShell. In fact, you have to go out of your way to convert a $LASTEXITCODE that represents an error to a PowerShell error. – Keith Hill Sep 08 '09 at 14:08
  • Could you at least post some code? Usually failure is not the default behavior in that case. – Joey Sep 08 '09 at 18:29
  • I'm running curl.exe and getting this: + CategoryInfo : NotSpecified: ( % Total % ... Time Current:String) [], RemoteException + FullyQualifiedErrorId : NativeCommandError – ripper234 Sep 09 '09 at 12:32
  • Interesting. Apparently you are trying this via V2 Remoting? It seems that a native command can cause a PowerShell error in this case. I'm looking into it. – Keith Hill Sep 10 '09 at 00:03
  • I'm not familiar with V2 remoting. I'm trying to run a local script containing the call to curl. – ripper234 Sep 10 '09 at 07:26
  • Apparently, as you point out, it is not just remoting but also ISE that generates an error if there is any output on stderr. It doesn't matter what the exit code is. I'm hoping to get some clarification from the PowerShell team as to why this is the case. – Keith Hill Sep 12 '09 at 03:24
  • There is example code for how to mask Powershell's peculiar output when an application writes to the Error stream, available here http://stackoverflow.com/q/26872205/80161 . A problem that probably could use its own question and answer, rather than one for each utility someone struggles with. – Nathan Hartley Jul 21 '16 at 16:07

6 Answers6

21

This doesn't have anything to do with the exit code returned by the EXE. An error is generated when an EXE writes to stderr, but only within the ISE or when remoting or using background jobs.

An EXE that writes to stderr does not generate errors from the regular PowerShell command prompt. I'm not sure why this is the case.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Keith Hill
  • 194,368
  • 42
  • 353
  • 369
  • 1
    In the shell it seems that stderr goes right to the console. Though if you reroute it to stdout (2>&1), it will wrap those messages in ErrorRecords. Maybe the PowerShell team decided that ISE was the kind of environment that all errors should be more noticable? – JasonMArcher Sep 14 '09 at 23:38
18

Actually, the application ran fine - PowerShell is mistaken in reporting an error.

When an application prints to standard error, PowerShell will sometimes conclude that application has failed. This is actually a design decision made by PowerShell developers. IMHO, this is a mistake, because many reliable applications (such as curl) print useful information to standard error in the course of normal operation. The consequence is that PowerShell only plays well with other PowerShell scripts and can't be relied on to interoperate with other applications.


Other readers in this thread had difficulty reproducing the behaviour because PowerShell implements it inconsistently. Whether the NativeCommandError occurs depends on how standard error is redirected (as a consequence the bug occurs in vanilla PowerShell ISE, but not vanilla PowerShell).

Whatever your opinion of the design decision in the first paragraph, the inconsistent implementation is for certain a PowerShell bug - see $LastExitCode=0 but $?=False in PowerShell. Redirecting stderr to stdout gives NativeCommandError.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
Colonel Panic
  • 132,665
  • 89
  • 401
  • 465
  • 2
    If curl is writing to `standard error` when there is no error, the conclusion must be that curl isn't playing well with others, and can't be relied on to interoperate with other applications. That PowerShell treats an error message as an error message in good faith, is not evidence that PowerShell is misbehaving and unreliable. – TessellatingHeckler Mar 14 '19 at 23:37
  • 7
    @TessellatingHeckler: For 40 years or so, stderr is used for "not the regular ouput but info for the user". This allows redirecting stdout to a file or other program and still see logging output. Would stdlog or something else be better? Yes. But we are stuck with the name. This is known behavior for several decades. It's Powershells fault ignoring this knowledge and implementing the current behavior (in a faulty way) out of the blue. For actually reporting an error, there is a thing called return codes... – Peter Schneider May 08 '19 at 11:17
  • Applications indicate failure to the caller by returning an error code. Assuming failure based only on writing to stderr is incorrect. – Mike Collins Jan 05 '22 at 22:44
2

I was running the script through PowerShell ISE (an IDE), and I believe this is what caused the problems.

Running it through PowerShell itself seems to work.

Peter Mortensen
  • 30,738
  • 21
  • 105
  • 131
ripper234
  • 222,824
  • 274
  • 634
  • 905
1

The error mentioned in the original post ("...NativeCommandError") appears when re-directing stderr to stdout, either e.g. by "...>file 2>&1", or by "*>file". As mentioned above, PowerShell produces this error if something is output via stderr, even if that 'something' is just normal logs (no errors). "CMake" e.g. uses stderr as normal trace output stream. While old Windows Command shell/script does NOT interpret any content on stderr as evidence of an error, PowerShell does, if the executable (e.g. CMake) is invoked with "Invoke-Expression", or called directly.

But!... if the executable is invoked with "Start-Process", this same problem does NOT occur.

Concretely, case 1:

Invoke-Expression cmake .... 1>$logFile1 2>$logFile2

OR

Invoke-Expression cmake .... *>$logFile

This leads to the above error

Case 2:

Start-Process cmake -ArgumentList $arguments ... -RedirectStandardOutput $logFile1 -RedirectStandardError $logFile2

The error doesn't appear. The files are 'clean', but two output files are populated

Conclusion: PowerShell behaves differently depending on how the executable is invoked

Powershell should preferably behave like the (old) Windows command shell, given the executables that are out there and that need to be run without experiencing such problems. The exit codes should hopefully suffice for qualifying errors. If this is really not possible, this feature of PowerShell - interpreting anything via stderr as error - should be switchable. Last but not least, the inconsistent behavior in that respect, and among the different means of invoking an executable, would best be avoided.

If you don't like streaming into two separate files as proposed in the case of 'Start-Process', the solution that works is to invoke the executable in the old command shell context, by use of "cmd /c" in the PowerShell script. Then you get both stdout and stderr streamed into ONE SINGLE file, AND properly populated (that is interleaved) by both streams as the executable operates. Example:

cmd /c "cmake ... >$logFile 2>&1"
  • I was starting to think the days of cmd are over and then I'm reading this after experiencing this amazing quirk. Thanks for the good laugh – Yorai Levi Dec 30 '21 at 23:08
1

Since Powershell and Powershell ISE have different ways of processing and handling error output, I've found that the best approach in our environment is to use a command execution wrapper using the old Windows cmd.exe as follows:

$output = $(cmd /c "<<your exe here with parameters>>  2>&1")

This method does not require capturing the output to a file.

Once the process completes, the error status is available in the PowerShell variable:

$LASTEXITCODE
Mars
  • 195
  • 1
  • 10
0

Sample script:

echo top
cmd /c dir foo   # doesn't exist
echo bottom

If run through the ISE, invoke-command, start-job, or start-threadjob, anything written to standard error will generate a remote exception. But the script will continue to run. This only applies to Powershell 5.

top
 Volume in drive C is DISK
 Volume Serial Number is 0123-4567

 Directory of C:\Users\js

cmd : File Not Found
At C:\Users\js\script.ps1:2 char:1
+ cmd /c dir foo 2>&1
+ ~~~~~~~~~~~~~~~~~~~
    + CategoryInfo          : NotSpecified: (File Not Found:String) [], RemoteException
    + FullyQualifiedErrorId : NativeCommandError

bottom

One option is to hide the error with 2>$null

echo top
cmd /c dir foo 2>$null
echo bottom

Resulting in:

top
 Volume in drive C is DISK
 Volume Serial Number is 0123-4567

 Directory of C:\Users\js

bottom
js2010
  • 23,033
  • 6
  • 64
  • 66