{"record":{"id":"92ee0330aacd357b","repo":"openai/codex","slug":"plugin-cache-root-should-be-absolute-err","errorCode":null,"errorMessage":"plugin cache root should be absolute: {err}","messagePattern":"plugin cache root should be absolute: (.+?)","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"codex-rs/core-plugins/src/store.rs","lineNumber":104,"sourceCode":"            return Err(PluginStoreError::Invalid(\n                \"invalid remote plugin install metadata: remote plugin id must not be blank\"\n                    .to_string(),\n            ));\n        }\n        Ok(Some(remote_plugin_id.to_string()))\n    }\n}\n\n#[derive(Clone, Copy)]\nenum InstallManifest<'a> {\n    OnDisk,\n    Fallback(&'a str),\n}\n\nimpl PluginStore {\n    pub fn new(codex_home: PathBuf) -> Self {\n        Self::try_new(codex_home)\n            .unwrap_or_else(|err| panic!(\"plugin cache root should be absolute: {err}\"))\n    }\n\n    pub fn try_new(codex_home: PathBuf) -> Result<Self, PluginStoreError> {\n        let root = AbsolutePathBuf::from_absolute_path_checked(codex_home.join(PLUGINS_CACHE_DIR))\n            .map_err(|err| PluginStoreError::io(\"failed to resolve plugin cache root\", err))?;\n        let data_root =\n            AbsolutePathBuf::from_absolute_path_checked(codex_home.join(PLUGINS_DATA_DIR))\n                .map_err(|err| PluginStoreError::io(\"failed to resolve plugin data root\", err))?;\n        let codex_home = AbsolutePathBuf::from_absolute_path_checked(codex_home)\n            .map_err(|err| PluginStoreError::io(\"failed to resolve Codex home\", err))?;\n\n        Ok(Self {\n            codex_home,\n            root,\n            data_root,\n        })\n    }\n","sourceCodeStart":86,"sourceCodeEnd":122,"githubUrl":"https://github.com/openai/codex/blob/339751715c64496cb86246bfb3935f40e309dd3d/codex-rs/core-plugins/src/store.rs#L86-L122","documentation":"PluginStore::new panics (unwrap_or_else(panic!)) when try_new fails. try_new runs AbsolutePathBuf::from_absolute_path_checked over codex_home and joined subdirectories (plugin cache/data roots); from_absolute_path_checked rejects any non-absolute path, so the panic fires when the supplied codex_home PathBuf is relative (e.g. \"codex-home\", \".codex\", or empty). It is a hard process abort by design: callers are expected to hand in an absolute CODEX_HOME.","triggerScenarios":"Calling PluginStore::new(PathBuf::from(<relative path>)) — any codex_home lacking a root component (no leading '/' on Unix or drive prefix on Windows); also a default-constructed/empty PathBuf.","commonSituations":"Tests constructing the store with a tempdir-relative path instead of its absolute form; reading CODEX_HOME from an env var that happens to be relative; refactors that pass a config string where an absolute path was expected; CLI invocations with a relative --codex-home flag.","solutions":["Canonicalize before constructing: std::fs::create_dir_all(&home).ok(); let home = std::fs::canonicalize(&home)?; then PluginStore::new(home)","Prefer PluginStore::try_new(...) when a graceful Result is needed instead of a panic","Validate early: assert/return an error if !codex_home.is_absolute() at the boundary where the value enters (env var / CLI flag parsing)","Normalize relative values by joining onto the current dir or the user home before use"],"exampleFix":"// before\nlet store = PluginStore::new(PathBuf::from(\".codex\")); // panics: plugin cache root should be absolute\n\n// after\nlet home = std::env::current_dir()?.join(\".codex\");\nlet store = PluginStore::try_new(home)?; // or PluginStore::new() with a proven-absolute path","handlingStrategy":"validation","validationCode":"// Before constructing the store:\nlet home = if codex_home.is_absolute() {\n    codex_home\n} else {\n    std::env::current_dir()?.join(codex_home)\n};\nlet store = PluginStore::try_new(home)?; // graceful instead of panic","typeGuard":"fn is_absolute_codex_home(p: &std::path::Path) -> bool {\n    p.is_absolute()\n}","tryCatchPattern":"// Prefer the Result API; if you must keep new(), catch_unwind only at a hard boundary:\nlet store = std::panic::catch_unwind(|| PluginStore::new(codex_home))\n    .map_err(|p| downcast_panic_message(p))?; // best: switch to try_new","preventionTips":["Always canonicalize or absolutize CODEX_HOME (env var, CLI flag, test fixture) before use","Use PluginStore::try_new in any context where bad input is possible","Assert codex_home.is_absolute() in debug builds at the config-loading boundary","Watch for relative paths sneaking in via tempdir helpers in tests — join with current_dir first"],"tags":["rust","codex","plugins","panic","absolute-path","config-validation"],"backgroundTag":"relative-path-where-absolute-required","analyzedSha":"339751715c64496cb86246bfb3935f40e309dd3d","analyzedAt":"2026-08-25T05:35:09.876Z","schemaVersion":2},"datasetVersion":"2026-08-25T06:17:31.827Z"}