{"record":{"id":"0d8876cc1f7b4f2c","repo":"Hmbown/CodeWhale","slug":"cleanup-failed","errorCode":"cleanup_failed","errorMessage":"docker rm -f ${container} failed: ${trim(r.stderr)}","messagePattern":"docker rm -f (.+?) failed: (.+?)","errorType":"error_code","errorClass":"SpawnError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/spawn.mjs","lineNumber":132,"sourceCode":"    throw err;\n  }\n}\n\n/**\n * Destroy a spawned container — but only one this plugin created. A docker\n * computer whose container lacks our spawn label is left running and reported\n * not_spawned; removing its registry entry is still the caller's choice.\n */\nexport async function destroyDockerComputer(computer) {\n  const container = computer?.container;\n  if (!container || !CONTAINER_RE.test(container)) {\n    throw new SpawnError(\"invalid_container\", \"docker computer has no valid container name\");\n  }\n  const insp = await docker([\"container\", \"inspect\", \"--format\", `{{index .Config.Labels \"${SPAWN_LABEL}\"}}`, container], { timeoutMs: 10_000, signal: null });\n  if (insp.code !== 0) return { destroyed: false, reason: \"container_gone\" };\n  if (insp.stdout.trim() !== \"1\") return { destroyed: false, reason: \"not_spawned\" };\n  const r = await docker([\"rm\", \"-f\", container], { timeoutMs: 20_000, signal: null });\n  if (r.code !== 0) throw new SpawnError(\"cleanup_failed\", `docker rm -f ${container} failed: ${trim(r.stderr)}`, r);\n  return { destroyed: true };\n}\n\n/**\n * Session teardown: destroy every spawned container this MCP process owns.\n * Other sessions' spawns are left alone — the registry is shared but a\n * container belongs to the process that created it.\n */\nexport async function destroySessionSpawns() {\n  const listed = await docker([\"ps\", \"-aq\", \"--filter\", `label=${SPAWN_LABEL}=1`, \"--filter\", `label=${SESSION_LABEL}=${SESSION_ID}`], { timeoutMs: 10_000, signal: null });\n  if (listed.code !== 0) return { destroyed: [], error: trim(listed.stderr) };\n  const ids = listed.stdout.split(\"\\n\").map((s) => s.trim()).filter(Boolean);\n  const destroyed = [];\n  for (const container of ids) {\n    const r = await docker([\"rm\", \"-f\", container], { timeoutMs: 15_000, signal: null });\n    destroyed.push({ container, ok: r.code === 0 });\n  }\n  return { destroyed };","sourceCodeStart":114,"sourceCodeEnd":150,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/spawn.mjs#L114-L150","documentation":"Thrown by destroyDockerComputer() after the container passed the spawn-label check but `docker rm -f <container>` exits non-zero. The docker CLI's stderr is embedded in the message. The container passed both name validation and the SPAWN_LABEL ownership check, so this is a genuine removal failure.","triggerScenarios":"destroyDockerComputer() where the container exists, its spawn label is \"1\", but `docker rm -f` fails — e.g. the container is paused/unremovable, the daemon died mid-call, or permissions changed between inspect and rm.","commonSituations":"Docker daemon restarting under load, container in a stuck state (`docker kill` first), rootless-docker permission quirks, or DOCKER_HOST/context switched between calls.","solutions":["Read the embedded stderr and run `docker rm -f <container>` manually to see the full error.","If the container is stuck: `docker kill <container>` then `docker rm -f <container>`.","Restart the Docker daemon if it became unresponsive, then retry destroy.","Verify no DOCKER_HOST/context switch occurred between spawn and destroy."],"exampleFix":"// before\n// Error: docker rm -f cu-spawn-x-abc failed: ... is not running\n// after\n$ docker kill cu-spawn-x-abc && docker rm -f cu-spawn-x-abc\n// then remove the stale registry entry","handlingStrategy":"try-catch","validationCode":"import { execFile } from \"node:child_process\";\nimport { promisify } from \"node:util\";\nconst pexec = promisify(execFile);\nconst { stdout } = await pexec(\"docker\", [\"container\", \"inspect\", \"--format\", \"{{.State.Status}}\", container]);\n// if stdout is \"paused\" or \"restarting\", fix state before destroy","typeGuard":null,"tryCatchPattern":"try {\n  await destroyDockerComputer(computer);\n} catch (e) {\n  if (e?.code === \"cleanup_failed\") {\n    // run `docker kill` then `docker rm -f` manually; then drop the registry entry\n  }\n}","preventionTips":["Keep the daemon healthy across spawn/destroy (avoid daemon restarts mid-session).","Don't switch DOCKER_HOST/context between spawn and destroy.","If containers get stuck, kill before force-removing.","Run rootless docker with consistent user/permissions."],"tags":["docker","cleanup","container"],"backgroundTag":"docker-command-failed","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}