{"record":{"id":"f30f4e0fc131ec46","repo":"Hmbown/CodeWhale","slug":"invalid-container-docker-computer-has-no-valid-name","errorCode":"invalid_container","errorMessage":"docker computer has no valid container name","messagePattern":"docker computer has no valid container name","errorType":"error_code","errorClass":"SpawnError","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/src/spawn.mjs","lineNumber":126,"sourceCode":"      if (Date.now() >= deadline) break;\n      await new Promise((resolve) => setTimeout(resolve, 500));\n    }\n    throw new SpawnError(\"spawn_failed\", `spawned desktop did not come up: ${lastErr}`);\n  } catch (err) {\n    await cleanup();\n    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);","sourceCodeStart":108,"sourceCodeEnd":144,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/src/spawn.mjs#L108-L144","documentation":"Thrown by destroyDockerComputer() when the computer object's container field is missing or fails CONTAINER_RE validation. The library only removes containers whose names match its own spawn naming scheme, as a safety guard against destroying arbitrary containers. This indicates a corrupted or hand-built computer record rather than a docker problem.","triggerScenarios":"destroyDockerComputer(computer) called with undefined/null computer, computer.container missing, or a container name that does not match the plugin's expected pattern (CONTAINER_RE) — e.g. a name from an older plugin version or fabricated by the caller.","commonSituations":"Passing a plain string instead of a computer object, persisting the computer record with a truncated/renamed container field, mixing records from a different tool version, or a stale registry entry after manual `docker rename`.","solutions":["Check the computer object actually has a `container` string property before destroying.","Use the computer object exactly as returned by spawnDockerComputer — don't hand-edit the container name.","If the record is corrupt/stale, remove its registry entry instead of calling destroy (per the code's own comment).","Confirm the container name matches the plugin's scheme (`cu-spawn-<id>-<hex>`); if it was renamed manually, destroy it with plain `docker rm -f` yourself."],"exampleFix":"// before\nawait destroyDockerComputer({ container: \"my_desktop\" }); // fails CONTAINER_RE\n// after\nconst computer = await spawnDockerComputer();\nawait destroyDockerComputer(computer); // container name from spawn, matches pattern","handlingStrategy":"type-guard","validationCode":"const CONTAINER_RE = /^[A-Za-z0-9][A-Za-z0-9_.-]*$/;\nfunction isDestroyable(computer) {\n  return typeof computer?.container === \"string\" && CONTAINER_RE.test(computer.container);\n}","typeGuard":"function isSpawnedComputer(c) {\n  return typeof c === \"object\" && c !== null &&\n    typeof c.container === \"string\" && /^cu-spawn-.+$/.test(c.container);\n}","tryCatchPattern":"try {\n  await destroyDockerComputer(computer);\n} catch (e) {\n  if (e?.code === \"invalid_container\") {\n    // the record is corrupt; drop its registry entry instead\n  }\n}","preventionTips":["Always pass the computer object returned by spawnDockerComputer unchanged.","Never hand-edit or truncate the container field when persisting records.","Validate computer records deserialized from disk/registry before destroying.","If a container was renamed manually, remove it with plain `docker rm -f`."],"tags":["validation","container","argument"],"backgroundTag":"invalid-argument-format","analyzedSha":"73e0f67d83c59909b571efdfc88c4bc28c309cb1","analyzedAt":"2026-09-22T01:30:00.501Z","contentChangedAt":"2026-09-22T01:30:00.501Z","schemaVersion":2},"datasetVersion":"2026-09-22T11:17:16.035Z"}