redis/node-redis · warning · ScanIteratorInterruptedError
Scan iteration was interrupted by a Sentinel master change
Error message
Scan iteration was interrupted by a Sentinel master change
What it means
The first of three throw sites in RedisSentinel.scanIterator: at the top of each loop iteration it checks the masterChanged flag set by the 'topology-change' MASTER_CHANGE listener. If a failover was observed between yielding one page and starting the next SCAN, it throws ScanIteratorInterruptedError because the cursor from the old master no longer applies.
Source
Thrown at packages/client/lib/sentinel/index.ts:723
* @throws {ScanIteratorInterruptedError} On observed `MASTER_CHANGE`.
*/
async *scanIterator(
this: RedisSentinelType<M, F, S, RESP, TYPE_MAPPING>,
options?: ScanOptions & ScanIteratorOptions
) {
let cursor: RedisArgument = options?.cursor ?? '0';
let masterChanged = false;
const handleTopologyChange = (event: RedisSentinelEvent) => {
if (event.type === 'MASTER_CHANGE') {
masterChanged = true;
}
};
this.on('topology-change', handleTopologyChange);
try {
do {
if (masterChanged) throw new ScanIteratorInterruptedError();
// Route through _execute so reserveClient:true reuses the reserved
// lease (instead of waiting forever on an empty master pool), and the
// lease is released before yielding — consumers can issue other
// commands inside the for-await loop without exhausting the pool.
let reply;
try {
reply = await this._execute(
false,
client => {
// Re-check after the lease resolves: a failover may have landed
// while waiting on an empty master pool, in which case the lease
// now points to a fresh client on the new master and SCAN would
// resume with a cursor from the old master.
if (masterChanged) throw new ScanIteratorInterruptedError();
return (client as RedisClientType<RedisModules, RedisFunctions, RedisScripts, RespVersions, TypeMapping>).scan(cursor, options);
}
);View on GitHub (pinned to bb5beb5657)
Solutions
- Catch ScanIteratorInterruptedError and restart the scan from the beginning (fresh scanIterator call).
- Run large scans outside failover windows or with idempotent dedup of keys.
- Reduce per-page processing time to shrink the window in which a failover can interrupt.
- Use the iterator's options.cursor only within a single uninterrupted run.
Example fix
// before
for await (const keys of sentinel.scanIterator()) { process(keys); } // throws mid-scan
// after
async function fullScan() {
for (;;) {
try { for await (const keys of sentinel.scanIterator()) { process(keys); } return; }
catch (e) { if (e instanceof ScanIteratorInterruptedError) continue; throw e; }
}
} Defensive patterns
Strategy: retry
Validate before calling
// Cannot pre-validate an asynchronous failover. Restart-on-interrupt:
async function robustScan(sentinel, process) { for (;;) { try { for await (const k of sentinel.scanIterator()) process(k); return; } catch (e) { if (!(e instanceof ScanIteratorInterruptedError)) throw e; } } } Type guard
function isScanInterrupted(e) { return e instanceof Error && /interrupted by a Sentinel master change/.test(e.message); } Try / catch
try { for await (const keys of sentinel.scanIterator()) process(keys); } catch (e) { if (isScanInterrupted(e)) { /* restart from new scanIterator */ } else throw e; } Prevention
- Catch ScanIteratorInterruptedError and restart the scan.
- Make per-key work idempotent and dedupe across restarts.
- Run big scans outside failover windows.
When it happens
Trigger: Iterating for await (const keys of sentinel.scanIterator()) and a Sentinel master failover event arrives between pages; the next iteration's pre-check sees masterChanged=true and aborts before issuing SCAN.
Common situations: Long-running full-key scans over a Sentinel-managed topology that fails over (maintenance, crash, manual failover) mid-iteration; scans running during a scheduled failover window.
Related errors
- SCAN: node ${address} serving this cursor has left the clust
- One (or more) of the watched keys has been changed
- FT.CURSOR: the node serving cursor ${token} on index "${argT
- SCAN: no master nodes available
- no valid master node
AI-assisted analysis of redis/node-redis@bb5beb5657 (2026-08-03).
Data as JSON: /data/errors/4235c1c5ca0abc2a.json.
Report an issue: GitHub.