{"record":{"id":"c13cc8873a8c8266","repo":"BigPizzaV3/CodexPlusPlus","slug":"only-user-scripts-can-be-deleted","errorCode":null,"errorMessage":"only user scripts can be deleted","messagePattern":"only user scripts can be deleted","errorType":"validation","errorClass":"anyhow::Error","httpStatus":null,"severity":"error","filePath":"crates/codex-plus-core/src/user_scripts.rs","lineNumber":115,"sourceCode":"    pub fn set_global_enabled(&self, enabled: bool) -> anyhow::Result<UserScriptConfig> {\n        let _guard = self.config_lock.lock().unwrap();\n        let mut config = self.load_config_unlocked();\n        config.enabled = enabled;\n        self.save_config_unlocked(&config)?;\n        Ok(config)\n    }\n\n    pub fn set_script_enabled(&self, key: &str, enabled: bool) -> anyhow::Result<UserScriptConfig> {\n        let _guard = self.config_lock.lock().unwrap();\n        let mut config = self.load_config_unlocked();\n        config.scripts.insert(key.to_string(), enabled);\n        self.save_config_unlocked(&config)?;\n        Ok(config)\n    }\n\n    pub fn delete_user_script(&self, key: &str) -> anyhow::Result<UserScriptConfig> {\n        let Some(file_name) = key.strip_prefix(\"user:\").filter(|value| !value.is_empty()) else {\n            anyhow::bail!(\"only user scripts can be deleted\");\n        };\n        if file_name.contains(['/', '\\\\']) || file_name == \".\" || file_name == \"..\" {\n            anyhow::bail!(\"invalid user script key\");\n        }\n        let path = self.user_dir.join(file_name);\n        let canonical_user_dir = self\n            .user_dir\n            .canonicalize()\n            .or_else(|_| {\n                fs::create_dir_all(&self.user_dir)?;\n                self.user_dir.canonicalize()\n            })\n            .with_context(|| {\n                format!(\n                    \"failed to resolve user script directory {}\",\n                    self.user_dir.display()\n                )\n            })?;","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/BigPizzaV3/CodexPlusPlus/blob/1f431ae49b57b3055e0e6845ba6156c6b4232b4d/crates/codex-plus-core/src/user_scripts.rs#L97-L133","documentation":"UserScriptManager::delete_user_script only manages user-provided scripts. The key must start with \"user:\" and have a non-empty remainder; the prefix is stripped and the remainder is used as a file name inside the user scripts directory. Keys without the prefix (built-in or market scripts) or the literal \"user:\" are rejected with \"only user scripts can be deleted\" before any filesystem access happens.","triggerScenarios":"delete_user_script(\"builtin:translate\"), delete_user_script(\"some-market-script\"), or delete_user_script(\"user:\") — passing a key taken from the builtin/market listing instead of the user-scripts listing, or an empty user script id.","commonSituations":"A UI passing the selected script's key from a mixed list without checking its origin; configs migrated between sections so keys lose their prefix; cleanup automation iterating over ALL script keys including built-ins.","solutions":["Only pass keys that came from the user-scripts listing (they carry the user: prefix)","Filter keys before calling: skip anything that does not strip to a non-empty user: remainder","Ensure the UI never offers an empty user script id (the literal \"user:\" case)"],"exampleFix":"// before\nmanager.delete_user_script(&selected_key)?;\n\n// after\nif selected_key.strip_prefix(\"user:\").is_some_and(|rest| !rest.is_empty()) {\n    manager.delete_user_script(&selected_key)?;\n} else {\n    log::warn!(\"refusing to delete non-user script {selected_key}\");\n}","handlingStrategy":"type-guard","validationCode":"if !is_deletable_user_script_key(&key) {\n    anyhow::bail!(\"refusing to delete non-user script key: {key}\");\n}\nlet config = manager.delete_user_script(&key)?;","typeGuard":"fn is_deletable_user_script_key(key: &str) -> bool {\n    key.strip_prefix(\"user:\").is_some_and(|rest| !rest.is_empty())\n}","tryCatchPattern":null,"preventionTips":["Only feed delete_user_script with keys obtained from the user-scripts listing","Keep builtin, market, and user keys in distinct UI collections so origins are unambiguous","Never synthesize keys by string concatenation across script kinds"],"tags":["user-scripts","key-validation","delete"],"backgroundTag":"invalid-key-prefix","analyzedSha":"1f431ae49b57b3055e0e6845ba6156c6b4232b4d","analyzedAt":"2026-08-16T20:54:18.598Z","schemaVersion":2},"datasetVersion":"2026-08-16T23:17:17.608Z"}