{"record":{"id":"79237a99c5bc4cb7","repo":"tonhowtf/omniget","slug":"external-data-cache-plugin-id-must-not-be-empty","errorCode":null,"errorMessage":"external_data_cache: plugin_id must not be empty","messagePattern":"external_data_cache: plugin_id must not be empty","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"src-tauri/src/plugin_host.rs","lineNumber":115,"sourceCode":"        let bin_name = _tool.to_string();\n\n        let managed_path = managed_dir.join(&bin_name);\n        if managed_path.exists() {\n            return Some(managed_path);\n        }\n\n        which::which(&bin_name).ok()\n    }\n\n    fn default_output_dir(&self) -> PathBuf {\n        dirs::download_dir()\n            .or_else(dirs::home_dir)\n            .unwrap_or_else(|| PathBuf::from(\".\"))\n    }\n\n    fn external_data_cache(&self, plugin_id: &str, namespace: &str) -> anyhow::Result<PathBuf> {\n        if plugin_id.is_empty() {\n            anyhow::bail!(\"external_data_cache: plugin_id must not be empty\");\n        }\n        if namespace.is_empty() {\n            anyhow::bail!(\"external_data_cache: namespace must not be empty\");\n        }\n        if plugin_id.contains(['/', '\\\\', ':', '\\0']) || namespace.contains(['/', '\\\\', ':', '\\0'])\n        {\n            anyhow::bail!(\n                \"external_data_cache: plugin_id/namespace must not contain path separators or null bytes\"\n            );\n        }\n\n        // portable installs keep every file next to the app, so the cache\n        // lives under the app data dir instead of the OS cache dir\n        let base = if std::env::var(\"OMNIGET_PORTABLE\").is_ok() {\n            omniget_core::core::paths::app_data_dir()\n                .ok_or_else(|| anyhow::anyhow!(\"external_data_cache: app data dir unavailable\"))?\n                .join(\"cache\")\n        } else {","sourceCodeStart":97,"sourceCodeEnd":133,"githubUrl":"https://github.com/tonhowtf/omniget/blob/8600b91f4246848bac346874daa9e61c1fc5677a/src-tauri/src/plugin_host.rs#L97-L133","documentation":"`external_data_cache` in plugin_host validates its `plugin_id` parameter before using it to build a cache directory path; an empty id would yield a malformed/ambiguous path. It bails via anyhow before any filesystem work.","triggerScenarios":"Calling plugin-host cache APIs with `plugin_id: \"\"` — typically a plugin manifest missing its id field, or code constructing the cache key from an absent/undefined field.","commonSituations":"Hand-edited or corrupted plugin manifest without `id`; migration/upgrade left the id field empty; caller passes a variable that defaulted to String::new().","solutions":["Ensure the plugin manifest declares a non-empty `id` before loading","Validate plugin_id at the loader boundary before invoking cache APIs","Default to a stable fallback id or reject the plugin at registration time"],"exampleFix":"// before\nlet cache = host.external_data_cache(&manifest.id, ns)?; // id may be \"\"\n// after\nensure!(!manifest.id.is_empty(), \"plugin manifest missing id\");\nlet cache = host.external_data_cache(&manifest.id, ns)?;","handlingStrategy":"validation","validationCode":"if plugin_id.is_empty() { return Err(anyhow!(\"plugin id required before cache access\")); }","typeGuard":"fn valid_plugin_id(id: &str) -> bool { !id.is_empty() && !id.contains(['/', '\\\\', ':', '\\0']) }","tryCatchPattern":"let cache = host.external_data_cache(id, ns)\n    .with_context(|| format!(\"cache path for plugin {id:?}\"))?;","preventionTips":["Require `id` in plugin manifests and validate at load","Never construct ids from optional fields without defaults","Fail fast at plugin registration"],"tags":["rust","anyhow","plugin","validation"],"backgroundTag":"empty-required-field","analyzedSha":"8600b91f4246848bac346874daa9e61c1fc5677a","analyzedAt":"2026-09-12T14:29:19.317Z","contentChangedAt":"2026-09-12T14:29:19.317Z","schemaVersion":2},"datasetVersion":"2026-09-15T23:17:13.987Z"}