skip to content

What makes a `newman run` process exit non-zero, and what does the `-x` flag change?

level: middleimportance: must knowfreq 74%

answer

  1. Two lines in the executable decide everything
  2. The failures array, not the status code
  3. Only ever zero or one
  4. One flag skips the assignment entirely
  5. exitCode is assigned, not exit called

basics

~20 s

The Newman CLI sets process.exitCode to 1 when the callback receives an error, when the summary carries a run error, or when the summary's failures list is non-empty. The -x / --suppress-exit-code flag skips that assignment, leaving the code 0.

solid answer

~40 s

After the run, Newman's command-line action computes `err || summary.run.error || summary.run.failures.length` and, when that is truthy, assigns `process.exitCode = 1`. `summary.run.failures` accumulates errors raised by run events — failed assertions, script errors, transport errors — so those are what make the command fail. An HTTP status is **not** one of them: a response of 500 that nothing asserts against leaves the failures list empty and the command exits 0. `-x` (`--suppress-exit-code`) sets `suppressExitCode`, which is read at exactly that one place and skips the assignment, so the process ends with 0 whatever happened. Note that Newman assigns `process.exitCode` rather than calling `process.exit`, so pending output is still flushed.

code

bash · 2 lines
bash
newman run ./collection.json -r cli,json --reporter-json-export ./out/report.json -x
echo "newman finished with $?"

go deeper

for a junior

Remember the two outcomes: 0 when nothing failed, 1 when something did. Know that -x is the flag that forces 0, and that it changes nothing else about the run.

for a middle

Explain the actual condition — an error, a run error, or a non-empty failures list — and be able to say what fills that list: assertions, script errors and transport errors, never a status code on its own.

for a senior

Show how you would catch a suite that cannot fail: count assertions in the summary, check that report artifacts exist, and be suspicious of any run where -x was added to quiet a red step.

for a principal

Own the policy on suppression. Decide where an informational run is legitimate, what consumes its report instead, and how the team prevents -x from spreading as the default cure for a noisy check.

## Where the exit code is decided The entire exit-code contract lives in two lines in the Newman executable, inside the callback given to the library entry point: ```javascript newman.run(options, function (err, summary) { const runError = err || summary.run.error || summary.run.failures.length; runError && !options.suppressExitCode && (process.exitCode = 1); }); ``` Three observations fall straight out of that: - The only non-zero code Newman itself produces is **1**. There is no code per failure category. - Newman assigns `process.exitCode`; it does not call `process.exit`. The process ends naturally once the event loop drains, so buffered reporter output is not truncated. - `suppressExitCode` is read here and **nowhere else in Newman**. It is purely a command-line concern; the library never sets an exit code at all. ## What counts as a failure `summary.run.failures` is an array the run summary fills by listening to the run's events. For each of `beforeIteration`, `iteration`, `beforeItem`, `item`, `beforeScript`, `script`, `beforePrerequest`, `prerequest`, `beforeRequest`, `request`, `beforeTest`, `test`, `beforeAssertion` and `assertion`, an error argument on the event is pushed as an entry recording the error, a `source` string naming where it happened, the item, its parent and the cursor. So the things that make the command fail are: - **a failed assertion** — the assertion event carries an error whenever an assertion did not pass, and the recorded source names the assertion's index; - **a script error** — a script that throws is recorded with its script type, line and column; - **a transport error** — a connection refused, a DNS failure, a timeout; - **a fatal run error**, which also reaches the callback as `err`. And the thing that does **not**: - **an HTTP status code on its own.** Newman never inspects a status to decide a failure. A collection whose only request receives 500, with nothing asserting on the response, produces an empty failures list and exits 0. That last point is the one that bites in practice. A collection with no assertions is, from the exit code's point of view, a collection that cannot fail — it only checks that the requests could be sent at all. ## What `-x` does, and what it costs | Situation | Without `-x` | With `-x` | |---|---|---| | Every assertion passed | exit 0 | exit 0 | | An assertion failed | exit 1 | exit 0 | | A script threw | exit 1 | exit 0 | | The request could not be sent | exit 1 | exit 0 | | A fatal run error occurred | exit 1 | exit 0 | `-x` does not change the run, the summary, the reports or the terminal output. Failures are still counted, still printed, still written into the JSON and XML reports. The flag only removes the one line that converts them into a process status. It also removes the only signal an automated caller has: after `-x`, the caller's sole way of learning that anything failed is to read a report file. ## Reading the result yourself When the run is driven from Node instead of a command line, none of the above happens for you. `newman.run` hands the callback `(err, summary)` and stops there. A wrapper that wants the same behaviour has to reproduce it: 1. Treat a non-null `err` as a fatal failure. 2. Check `summary.run.failures.length` for run failures. 3. Decide the process status yourself — and remember that assigning `process.exitCode` is friendlier than calling `process.exit`, which can cut off writes still in flight. The summary also carries counters under `summary.run.stats` — `iterations`, `items`, `scripts`, `prerequests`, `requests`, `tests`, `assertions`, `testScripts`, `prerequestScripts` — each with `total`, `failed` and `pending`, which is what a wrapper reads when it wants a threshold rather than a binary verdict. ## Practical guidance - Reach for `-x` only when the run is deliberately informational and something else consumes the report. Leaving it on permanently turns a check into decoration. - If a run is passing suspiciously often, count the assertions in the summary before trusting it; zero assertions means an exit code that can only ever be 0. - Never expect a code other than 0 or 1 from Newman itself, and do not encode meaning into the difference between failure kinds — they all collapse to 1. - In a Node wrapper, write the exit-code logic explicitly. Nothing inherits it.

  • A Newman run is green but the service was returning 500 the whole time. How is that possible?
    Newman derives the exit code from `summary.run.failures`, which is fed by run events — assertions, script errors, transport errors. A status code is never itself a failure. If the collection asserts nothing about the response, a 500 is simply a response that arrived, the failures list stays empty, and the command exits 0.
  • Does `-x` change anything besides the process exit code?
    No. The run executes identically, the failures are still recorded in the summary, and every reporter still prints and writes exactly what it would have. Only the single assignment of `process.exitCode` is skipped, which means an automated caller loses its one signal and must read a report file instead.
  • Which exit codes can Newman itself produce?
    0 and 1. The executable assigns `process.exitCode = 1` for any run error and leaves it at the default otherwise; there is no per-category code. Anything else you observe came from the shell or from the process being killed, not from Newman's own logic.

saying these in an interview costs you the question

  • Thinks a 5xx response by itself fails the run
  • Expects distinct exit codes for different failure kinds
  • Believes -x also hides failures from the reports
  • Assumes a Node wrapper sets the exit code automatically
  • Trusts a green run from a collection with no assertions
  • Thinks Newman calls process.exit rather than setting exitCode