--timeout has no effect on collections using setNextRequest()
Summary
newman --timeout (and the equivalent programmatic newman.run({ timeout }), which maps to postman-runtime's timeout.global) does not abort a run when the collection uses pm.execution.setNextRequest(<self>) to poll.
The runtime correctly fires its done(err) callback at the deadline with err.message = 'callback timed out', but does not halt its internal request queue. Newman keeps making HTTP requests indefinitely after telling the user the run is "done" — only an external SIGTERM stops it.
This caused a CI step in our pipeline to run for several hours before the agent's own step timeout killed it.
Environment
newman |
6.2.1 |
postman-runtime |
7.39.1 |
| Node.js | 22.21.0 |
| OS | Windows 11 (also reproduced on Ubuntu 22.04) |
Minimal reproduction
repro.postman_collection.json — a 1-request collection that re-queues itself:
{
"info": { "name": "repro", "schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json" },
"item": [{
"name": "Poll Self",
"request": { "method": "GET", "url": "https://postman-echo.com/get" },
"event": [{
"listen": "test",
"script": { "type": "text/javascript", "exec": [
"var n = Number(pm.variables.get('iter') || 0) + 1;",
"pm.variables.set('iter', n);",
"if (!pm.variables.get('startedAt')) { pm.variables.set('startedAt', Date.now()); }",
"console.log('iteration', n, 'elapsedMs', Date.now() - Number(pm.variables.get('startedAt')));",
"pm.execution.setNextRequest(pm.info.requestName);"
]}
}]
}]
}npm install [email protected]
newman run repro.postman_collection.json --timeout 10000Result: ran for 60 s, completed 197 iterations, never aborted. Killed externally. stderr received error: callback timed out from the runtime, yet iterations continued unabated.
Smoking gun (programmatic API)
This proves the callback fires on time but the runtime keeps the event loop busy:
const newman = require('newman');
const collection = require('./repro.postman_collection.json');
const t0 = Date.now();
const elapsed = () => ((Date.now() - t0) / 1000).toFixed(1);
let firedAt = null;
newman.run({ collection, timeout: 5000, reporters: [] }, (err) => {
firedAt = elapsed();
console.log(`[t=${firedAt}s] callback fired. err: ${err && err.message}`);
// intentionally NOT calling process.exit()
});
setInterval(() => {
if (firedAt) console.log(`[t=${elapsed()}s] WATCHDOG: callback fired at ${firedAt}s, but event loop still busy.`);
}, 2000);
setTimeout(() => { console.log(`[t=${elapsed()}s] HARD KILL`); process.exit(99); }, 25000);Output:
[t=5.0s] callback fired. err: callback timed out
[t=6.0s] WATCHDOG: callback fired at 5.0s, but event loop still busy.
[t=8.0s] WATCHDOG: callback fired at 5.0s, but event loop still busy.
...
[t=24.1s] WATCHDOG: callback fired at 5.0s, but event loop still busy.
[t=25.0s] HARD KILLdone(err) fires at exactly the deadline, but the runtime keeps dispatching setNextRequest iterations for the next 20 s until we hard-kill the process.
Expected behavior
When timeout.global fires, the runtime should also abort its internal request queue so no further requests are dispatched and the event loop can drain.
Actual behavior
Only the done callback fires. The request queue keeps running. The user-facing --timeout flag is effectively a no-op for setNextRequest polling collections.
Impact
Real production hazard for CI workflows that poll an async backend with setNextRequest. If the upstream service returns a non-terminal status indefinitely, the documented --timeout flag provides a false sense of safety — only an external process killer ends the run.
Source: postmanlabs/newman