{"record":{"id":"846185ddd85d685a","repo":"jlcodes99/cockpit-tools","slug":"codex-ssh-disable-sync-failed","errorCode":null,"errorMessage":"[Codex SSH] disable sync failed:","messagePattern":"\\[Codex SSH\\] disable sync failed:","errorType":"console","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"src/components/codex/CodexSshSyncSettingsControl.tsx","lineNumber":55,"sourceCode":"  const enabled = Boolean(\n    selectedServer && selectedServer.sync_on_codex_switch,\n  );\n\n  const openModal = useCallback(() => setModalOpen(true), []);\n  const closeModal = useCallback(() => setModalOpen(false), []);\n\n  const handleToggle = async (next: boolean) => {\n    if (busy) return;\n    if (next) {\n      setModalOpen(true);\n      return;\n    }\n    setBusy(true);\n    try {\n      // 关闭：取消选中，后端切号同步不会再推到任何 SSH 主机\n      await selectServer(null);\n    } catch (error) {\n      console.warn('[Codex SSH] disable sync failed:', error);\n    } finally {\n      setBusy(false);\n    }\n  };\n\n  const hintText = enabled && selectedServer\n    ? t('codex.ssh.syncSwitchActive', '已启用 · {{name}}（{{user}}@{{host}}）', {\n        name: selectedServer.name,\n        user: selectedServer.username,\n        host: selectedServer.host,\n      })\n    : t(\n        'codex.ssh.syncSwitchDesc',\n        '开启后配置 SSH 主机；Codex 切号时把当前账号同步到远端。',\n      );\n\n  if (variant === 'quick') {\n    return (","sourceCodeStart":37,"sourceCodeEnd":73,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/src/components/codex/CodexSshSyncSettingsControl.tsx#L37-L73","documentation":"`handleToggle` disables Codex SSH sync by calling `selectServer(null)`, telling the backend to stop pushing settings to any SSH host. If that backend call rejects, the failure is logged but the switch state may already reflect 'off', silently diverging from the backend.","triggerScenarios":"The `selectServer(null)` backend call rejects: SSH config write fails (permissions on known_hosts/ssh config), the backend command errors, or the IPC channel is closed.","commonSituations":"Read-only SSH config file or missing write permissions; partially removed SSH host entries; backend upgrade changing the sync API; calling toggle while a previous sync is in progress.","solutions":["Check the logged error for the backend rejection cause and fix filesystem permissions on the SSH config it writes.","Revert the toggle UI state to match the backend when the call fails, instead of leaving it optimistic.","Retry `selectServer(null)` after the user fixes permissions, then confirm via re-reading current server selection.","Ensure no concurrent sync operation is running when toggling."],"exampleFix":"// before\n} catch (error) {\n  console.warn('[Codex SSH] disable sync failed:', error);\n}\n// after\n} catch (error) {\n  console.warn('[Codex SSH] disable sync failed:', error);\n  setEnabled(true); // roll back optimistic UI state\n}","handlingStrategy":"fallback","validationCode":"const current = await getCurrentSelectedServer();\nif (current === null) return; // already disabled; skip the call","typeGuard":"function isSelectedServer(v: unknown): v is string | null {\n  return v === null || typeof v === 'string';\n}","tryCatchPattern":"try {\n  await selectServer(null);\n  setEnabled(false);\n} catch (error) {\n  console.warn('[Codex SSH] disable sync failed:', error);\n  setEnabled(true); // roll back optimistic state to match backend\n}","preventionTips":["Roll back optimistic toggle state when the backend call fails","Check SSH config file writability before toggling","Re-read backend selection state after each toggle to confirm sync","Avoid concurrent toggle/sync operations"],"tags":["ssh","sync","tauri","state-consistency"],"backgroundTag":"backend-write-failed","analyzedSha":"1ed8b77992d62ca81fabf744deb0839ad361d5bf","analyzedAt":"2026-09-05T09:51:41.178Z","contentChangedAt":"2026-09-05T09:51:41.178Z","schemaVersion":2},"datasetVersion":"2026-09-12T12:17:11.808Z"}