{"record":{"id":"b61978b68ddea7b7","repo":"jlcodes99/cockpit-tools","slug":"error-b61978","errorCode":null,"errorMessage":"无法获取用户目录","messagePattern":"无法获取用户目录","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"crates/cockpit-core/src/modules/codex_account_core_provider.rs","lineNumber":887,"sourceCode":"    // built-in `openai` provider with this account's own base URL and auth.json API key.\n    let builtin_openai_config = resolve_api_provider_config(\n        provider_config.base_url.as_deref(),\n        Some(CodexApiProviderMode::OpenaiBuiltin),\n        None,\n        None,\n    )?;\n    write_api_provider_to_config_toml(base_dir, &builtin_openai_config)\n}\n\n/// 旧版数据目录（~/Library/Application Support/com.antigravity.cockpit-tools/）\nfn get_old_codex_data_dir() -> PathBuf {\n    #[cfg(test)]\n    if let Some(data_dir) = std::env::var_os(\"COCKPIT_TOOLS_DATA_DIR\") {\n        return PathBuf::from(data_dir).join(\"legacy-codex-data\");\n    }\n\n    dirs::data_local_dir()\n        .unwrap_or_else(|| dirs::home_dir().expect(\"无法获取用户目录\"))\n        .join(\"com.antigravity.cockpit-tools\")\n}\n\n/// 将旧目录中的 codex 数据迁移到新目录（一次性，迁移成功后删除旧文件）\nfn migrate_codex_data_if_needed(new_data_dir: &PathBuf) {\n    #[cfg(test)]\n    if std::env::var_os(\"COCKPIT_TOOLS_DATA_DIR\").is_some() {\n        // Test guards use an explicit data root; never migrate the user's legacy files.\n        return;\n    }\n\n    let old_dir = get_old_codex_data_dir();\n    if !old_dir.exists() {\n        return;\n    }\n\n    // 迁移 codex_accounts.json\n    let old_index = old_dir.join(\"codex_accounts.json\");","sourceCodeStart":869,"sourceCodeEnd":905,"githubUrl":"https://github.com/jlcodes99/cockpit-tools/blob/1ed8b77992d62ca81fabf744deb0839ad361d5bf/crates/cockpit-core/src/modules/codex_account_core_provider.rs#L869-L905","documentation":"get_old_codex_data_dir resolves the legacy Codex data directory via dirs::data_local_dir(), falling back to dirs::home_dir().expect(\"无法获取用户目录\"). If both the local data dir and the home dir are unavailable it panics. It is used only by migrate_codex_data_if_needed for one-time migration.","triggerScenarios":"Calling migrate_codex_data_if_needed (which calls get_old_codex_data_dir) on a system where dirs::data_local_dir() and dirs::home_dir() both return None — no XDG/USERPROFILE environment and no resolvable home.","commonSituations":"Test/CI environments with a stripped environment (COCKPIT_TOOLS_DATA_DIR unset and no HOME); running under a service account without a profile; broken passwd/XDG configuration.","solutions":["Set HOME (Unix) or USERPROFILE/LOCALAPPDATA (Windows) so dirs can resolve a path.","Set COCKPIT_TOOLS_DATA_DIR (test builds) to bypass legacy-dir resolution.","Skip migration when no home directory can be resolved — migration is optional one-time work.","Run the app under a normal user account with a profile rather than a bare service account."],"exampleFix":"// before\nmigrate_codex_data_if_needed(&data_dir); // panics without HOME\n\n// after\nif std::env::var_os(\"HOME\").is_some()\n    || std::env::var_os(\"USERPROFILE\").is_some()\n{\n    migrate_codex_data_if_needed(&data_dir);\n} else {\n    eprintln!(\"no home dir; skipping legacy migration\");\n}","handlingStrategy":"validation","validationCode":"fn legacy_migration_env_ok() -> bool {\n    std::env::var_os(\"COCKPIT_TOOLS_DATA_DIR\").is_some()\n        || std::env::var_os(\"HOME\").is_some()\n        || std::env::var_os(\"USERPROFILE\").is_some()\n}","typeGuard":null,"tryCatchPattern":"std::panic::catch_unwind(get_old_codex_data_dir).map_err(|_| anyhow::anyhow!(\"无法获取用户目录\"))","preventionTips":["Skip legacy migration when no home directory can be resolved — it is optional one-time work","Set COCKPIT_TOOLS_DATA_DIR in test environments to isolate storage","Set HOME/LOCALAPPDATA in CI containers before running migration paths","Run migration only inside an interactive user session"],"tags":["panic","home-directory","migration","environment"],"backgroundTag":"missing-home-directory","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"}