{"record":{"id":"016d136e2670ba2b","repo":"Hmbown/CodeWhale","slug":"control-changed-but-its-restart-preference-could-not-be","errorCode":null,"errorMessage":"Control changed, but its restart preference could not be saved. Check disk space before reopening the app.","messagePattern":"Control changed, but its restart preference could not be saved\\. Check disk space before reopening the app\\.","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"crates/tui/plugins/computer-use/app/daemon.mjs","lineNumber":64,"sourceCode":"const leases = new Map();\nlet shuttingDown = false;\nlet backgroundCheck = null;\nlet checking = false;\nlet update = readUpdateResult();\nlet updating = false;\nlet controlError = null;\nconst controlFile = path.join(stateDir(), \"control.json\");\nasync function userControl(mode) {\n  const work = setControlMode(mode);\n  if (mode === \"ready\") await work;\n  const temporary = `${controlFile}.${process.pid}.tmp`;\n  let storageError;\n  try {\n    fs.writeFileSync(temporary, JSON.stringify({ mode }), { mode: 0o600 });\n    fs.renameSync(temporary, controlFile);\n  } catch (error) { storageError = error; }\n  const result = await work;\n  if (storageError) throw new Error(\"Control changed, but its restart preference could not be saved. Check disk space before reopening the app.\");\n  return result;\n}\ntry {\n  const saved = JSON.parse(fs.readFileSync(controlFile, \"utf8\"));\n  if (saved.mode !== \"ready\") await setControlMode([\"paused\", \"stopped\"].includes(saved.mode) ? saved.mode : \"stopped\");\n} catch (error) { if (error.code !== \"ENOENT\") await setControlMode(\"stopped\"); }\n\n// An inherited socketpair joins the menu-bar owner and its child. This has\n// no filesystem endpoint, no reusable credential and no MCP equivalent.\n// Losing the human control process fails closed before accepting more work.\nif (process.env.CODEWHALE_CU_CONTROL_FD === \"3\") {\n  const control = new net.Socket({ fd: 3, readable: true, writable: true });\n  controlOwner = true;\n  delete process.env.CODEWHALE_CU_CONTROL_FD;\n  let input = \"\";\n  control.setEncoding(\"utf8\");\n  const status = () => ({ ...controlStatus(), version: APP_VERSION, checking, backgroundCheck, error: controlError,\n    update: update ? { available: update.available, version: update.version, message: update.message, busy: updating } : null });","sourceCodeStart":46,"sourceCodeEnd":82,"githubUrl":"https://github.com/Hmbown/CodeWhale/blob/73e0f67d83c59909b571efdfc88c4bc28c309cb1/crates/tui/plugins/computer-use/app/daemon.mjs#L46-L82","documentation":"setControlMode persists the requested daemon control mode ('ready'/'paused'/'stopped') by atomically writing a JSON control file via writeFileSync + renameSync. If either write fails, the mode change is still applied in memory but the restart preference is lost, so a descriptive Error is thrown after the work completes to warn the user their preference will not survive restarts.","triggerScenarios":"Calling userControl (or the startup setControlMode('stopped') fallback) when the filesystem cannot write the control file: full disk, read-only volume, permission denied on the control file's directory, or renameSync failing across filesystems.","commonSituations":"Disk full on the machine running the computer-use daemon; app installed in a location without write permissions; antivirus/quarantine locking the temp file; temp write succeeds but rename fails because temporary and controlFile land on different mounts.","solutions":["Free disk space on the volume holding the control file, then retry the control change","Check write permissions on the directory containing controlFile and fix ownership/chmod","Ensure the temp file and controlFile are on the same filesystem so renameSync works","Check disk health / read-only mount status (diskutil verifyVolume on macOS) and restart the app"],"exampleFix":"// before (no preflight, fails at write time)\nfs.writeFileSync(temporary, JSON.stringify({ mode }), { mode: 0o600 });\n// after (preflight disk space before attempting the change)\nconst stat = fs.statfsSync(path.dirname(controlFile));\nif (stat.bavail * stat.bsize < 4096) throw new Error(\"Insufficient disk space to save control preference.\");\nfs.writeFileSync(temporary, JSON.stringify({ mode }), { mode: 0o600 });","handlingStrategy":"try-catch","validationCode":"const dir = path.dirname(controlFile);\nif (!fs.existsSync(dir)) throw new Error(\"control dir missing\");\nconst st = fs.statfsSync(dir);\nif (st.bavail * st.bsize < 4096) throw new Error(\"low disk space\");","typeGuard":null,"tryCatchPattern":"try {\n  await userControl(mode, work);\n} catch (e) {\n  if (e.message.includes(\"restart preference could not be saved\")) {\n    notifyUser(\"Preference not persisted; check disk space.\");\n  } else throw e;\n}","preventionTips":["Monitor free disk space on the volume hosting the control file","Keep the control file and its temp file on the same filesystem","Test write access to the control directory at app startup","Avoid syncing the control directory with tools that may lock or replace files"],"tags":["filesystem","persistence","disk-space","file-write"],"backgroundTag":"file-write-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"}