{"record":{"id":"d8284b8bfa832bf1","repo":"redis/node-redis","slug":"docker-rm-error-stderr","errorCode":null,"errorMessage":"docker rm error - ${stderr}","messagePattern":"docker rm error - (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"warning","filePath":"packages/test-utils/lib/dockers.ts","lineNumber":241,"sourceCode":"    dockerId: stdout.trim(),\n  };\n}\n\nexport function spawnRedisServer(dockerConfig: RedisServerDockerOptions, serverArguments: Array<string>): Promise<RedisServerDocker> {\n  const runningServer = RUNNING_SERVERS.get(serverArguments);\n  if (runningServer) {\n    return runningServer;\n  }\n\n  const dockerPromise = spawnRedisServerDocker(dockerConfig, serverArguments);\n  RUNNING_SERVERS.set(serverArguments, dockerPromise);\n  return dockerPromise;\n}\n\nasync function dockerRemove(dockerId: string): Promise<void> {\n  const { stderr } = await execAsync('docker', ['rm', '-f', dockerId]);\n  if (stderr) {\n    throw new Error(`docker rm error - ${stderr}`);\n  }\n}\n\n\n\n/**\n * Reads a file from a Docker container directly into memory\n * @param dockerId - The Docker container ID\n * @param filePath - Path to the file inside the container\n * @returns Buffer containing the file contents\n */\nasync function readFileFromContainer(\n  dockerId: string,\n  filePath: string,\n): Promise<Buffer> {\n  const { stdout, stderr } = await execAsync(\"docker\", [\n    \"exec\",\n    dockerId,","sourceCodeStart":223,"sourceCodeEnd":259,"githubUrl":"https://github.com/redis/node-redis/blob/90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58/packages/test-utils/lib/dockers.ts#L223-L259","documentation":"dockerRemove runs `docker rm -f <id>` and treats ANY non-empty stderr as fatal. This runs in after() hooks that clean up Redis/proxy/cluster/sentinel containers. Note that Docker sometimes writes warnings to stderr even on success, so this guard can fire on benign messages.","triggerScenarios":"Container already removed by a prior cleanup pass (double cleanup); container ID invalid/stale; Docker daemon error during forced removal; a non-fatal warning written to stderr.","commonSituations":"A test crashed mid-suite and partial cleanup already ran; the after() hook and an explicit teardown both target the same container; Docker emits a deprecation/policy warning to stderr.","solutions":["Read the stderr text in the message — if it is a 'No such container' notice, the container was already gone and the error is benign","Filter stderr for the known-benign 'No such container' substring before failing","Run docker ps -a to see what containers remain and reconcile state"],"exampleFix":"// before — any stderr aborts removal\nconst { stderr } = await execAsync('docker', ['rm', '-f', dockerId]);\nif (stderr) throw new Error(`docker rm error - ${stderr}`);\n\n// after — tolerate 'No such container'\nif (stderr && !/No such container/i.test(stderr)) {\n  throw new Error(`docker rm error - ${stderr}`);\n}","handlingStrategy":"try-catch","validationCode":"import { execFile } from 'node:child_process';\nimport { promisify } from 'node:util';\nconst exec = promisify(execFile);\nasync function containerExists(id: string): Promise<boolean> {\n  try { await exec('docker', ['inspect', id]); return true; } catch { return false; }\n}","typeGuard":null,"tryCatchPattern":"try {\n  await dockerRemove(dockerId);\n} catch (e) {\n  if (e instanceof Error && /docker rm error/.test(e.message) && /No such container/.test(e.message)) {\n    return; // already removed, benign\n  }\n  throw e;\n}","preventionTips":["Tolerate 'No such container' in stderr rather than failing cleanup","Track removed ids in a Set to avoid double cleanup","Run cleanup in after() hooks with this.timeout raised so slow removals don't time out"],"tags":["docker","test-utils","cleanup","container"],"backgroundTag":null,"analyzedSha":"90fd0652bc3f2a0a1b2f79fa9096b02a86b0ac58","analyzedAt":"2026-08-11T15:37:21.243Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}