{"record":{"id":"e1a703e3375204be","repo":"redis/node-redis","slug":"all-ports-are-in-use","errorCode":null,"errorMessage":"All ports are in use","messagePattern":"All ports are in use","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/test-utils/lib/dockers.ts","lineNumber":42,"sourceCode":"    await once(socket, 'connect');\n    socket.end();\n  } catch (err) {\n    if (err instanceof Error && (err as ErrorWithCode).code === 'ECONNREFUSED') {\n      return true;\n    }\n  }\n\n  return false;\n}\n\nconst portIterator = (async function* (): AsyncIterableIterator<number> {\n  for (let i = 6379; i < 65535; i++) {\n    if (await isPortAvailable(i)) {\n      yield i;\n    }\n  }\n\n  throw new Error('All ports are in use');\n})();\n\ninterface RedisServerDockerConfig {\n  image: string;\n  version: string;\n}\n\ninterface SentinelConfig {\n  mode: \"sentinel\";\n  mounts: Array<string>;\n  port: number;\n}\n\ninterface ServerConfig {\n  mode: \"server\";\n}\n\nexport type RedisServerDockerOptions = RedisServerDockerConfig & (SentinelConfig | ServerConfig)","sourceCodeStart":24,"sourceCodeEnd":60,"githubUrl":"https://github.com/redis/node-redis/blob/90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58/packages/test-utils/lib/dockers.ts#L24-L60","documentation":"portIterator is a module-singleton async generator that yields free TCP ports from 6379 to 65534, checking each via isPortAvailable (a connect attempt that returns true on ECONNREFUSED). When the loop exhausts the range with every port occupied, it throws. Note: once the generator throws it becomes closed, so subsequent .next() calls return {done:true, value:undefined} rather than re-throwing — callers then fail later with an undefined-port error.","triggerScenarios":"Running the test suite with many concurrent Docker containers, or leaving leaked Redis containers bound to host ports (the suite uses --network host), until the entire 6379–65534 range is occupied.","commonSituations":"A previous test run crashed without cleanup, leaving containers alive; CI runners with low port hygiene; many parallel test workers each consuming ports; another service (local Redis, dev DBs) squatting on the low range.","solutions":["Remove leaked containers: docker rm -f $(docker ps -aq) or docker ps then remove the redis/test images","Stop other local services occupying ports in the 6379+ range (local redis-server, postgres, etc.)","Reduce parallelism / worker count in the test runner so fewer ports are needed at once"],"exampleFix":"# before — ports exhausted, error thrown\n# (leaked containers from a prior run)\n\n# after — reclaim ports\ndocker rm -f $(docker ps -aq --filter 'ancestor=redislabs/client-libs-test')\n# rerun the suite","handlingStrategy":"validation","validationCode":"import { createConnection } from 'node:net';\nasync function countFreePorts(from = 6379, to = 65535, sample = 50): Promise<number> {\n  let free = 0;\n  for (let p = from; p < to && free < sample; p++) {\n    try {\n      const s = createConnection({ port: p });\n      await new Promise((res, rej) => { s.once('connect', () => { s.end(); res(null); }); s.once('error', rej); });\n    } catch { free++; }\n  }\n  return free;\n}","typeGuard":null,"tryCatchPattern":"try {\n  const docker = await spawnRedisServerDocker(opts, args);\n} catch (e) {\n  if (e instanceof Error && /All ports are in use/.test(e.message)) {\n  }\n  throw e;\n}","preventionTips":["Add a global before() hook that runs docker rm -f on leaked test containers","Cap test parallelism so the port range is not exhausted","Stop local services squatting on the 6379+ range before running the suite"],"tags":["docker","networking","ports","test-utils","resource-exhaustion"],"backgroundTag":null,"analyzedSha":"90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58","analyzedAt":"2026-08-11T15:37:21.243Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}