tonhowtf/omniget · error
plugin id contains an illegal character
Error message
plugin id contains an illegal character {bad:?}: {plugin_id:?} What it means
`validate_plugin_id` scans each character and bails if any is not ASCII alphanumeric or one of '.', '_', '-'. This keeps plugin ids safe as directory/file names on all platforms and prevents injection of separators or unicode lookalikes.
Solutions
- Restrict ids to [A-Za-z0-9._-] at creation time
- Normalize display names to slug form before using them as ids
- Reject the plugin at load time with this validation instead of failing later
Example fix
// before
let id = display_name; // "My Plugin!"
// after
let id: String = display_name.to_lowercase().chars().map(|c| if c.is_ascii_alphanumeric() { c } else { '_' }).collect(); Defensive patterns
Strategy: validation
Validate before calling
fn id_charset_ok(id: &str) -> bool { id.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '.' | '_' | '-')) } Type guard
fn is_ascii_slug(id: &str) -> bool { !id.is_empty() && id.chars().all(|c| c.is_ascii_alphanumeric() || matches!(c, '.' | '_' | '-')) } Try / catch
let dir = plugin_dir(&id).with_context(|| format!("illegal chars in plugin id {id:?}"))?; Prevention
- Slugify display names before using them as ids
- Restrict id input at UI level
- Validate on both creation and load
When it happens
Trigger: An id containing spaces, slashes, colons, accented/CJK characters, or other symbols is passed to plugin_dir/validate_plugin_id.
Common situations: Plugin id derived from a display name (e.g. "My Cool Plugin!"); id copy-pasted with whitespace; non-ASCII localized ids.
Understand the failure class
Background: "invalid id" errors: invalid identifier format — why libraries reject IDs before lookup, and how to fix them — this error's family across 37 libraries.
Related errors
- external_data_cache: plugin_id must not be empty
- external_data_cache: namespace must not be empty
- plugin id must not be empty
- external_data_cache: plugin_id/namespace must not contain…
- plugin id must not be a relative path component
AI-assisted analysis of tonhowtf/omniget@8600b91f42 (2026-09-12).
Data as JSON: /api/errors/ea8dd16e56611093.
Report an issue: GitHub.
Appendix: source
Thrown at src-tauri/src/plugin_loader.rs:242
/// Aceita apenas `[A-Za-z0-9._-]`, recusando separador de caminho, byte nulo,
/// componente relativo e id vazio.
///
/// Allowlist e nao blocklist de proposito: o conjunto de ids reais e pequeno e
/// conhecido (`courses`, `study`, `telegram`, `convert`, `misc`), e uma
/// blocklist erra por omissao a cada codificacao nova.
pub fn validate_plugin_id(plugin_id: &str) -> anyhow::Result<()> {
if plugin_id.is_empty() {
anyhow::bail!("plugin id must not be empty");
}
if plugin_id == "." || plugin_id == ".." || plugin_id.starts_with('.') {
anyhow::bail!("plugin id must not be a relative path component: {plugin_id:?}");
}
if let Some(bad) = plugin_id
.chars()
.find(|c| !c.is_ascii_alphanumeric() && !matches!(c, '.' | '_' | '-'))
{
anyhow::bail!("plugin id contains an illegal character {bad:?}: {plugin_id:?}");
}
if plugin_id.contains("..") {
anyhow::bail!("plugin id must not contain '..': {plugin_id:?}");
}
Ok(())
}
fn load_single_plugin(
plugin_dir: &Path,
host: Arc<dyn PluginHost>,
) -> Result<LoadedPlugin, PluginLoadError> {
let manifest_path = plugin_dir.join("plugin.json");
let manifest_str = fs::read_to_string(&manifest_path).map_err(|e| {
PluginLoadError::simple("manifest_read", format!("Cannot read plugin.json: {e}"))
})?;
let manifest: PluginManifest = serde_json::from_str(&manifest_str).map_err(|e| {
PluginLoadError::simple("manifest_parse", format!("Invalid plugin.json: {e}"))
})?;View on GitHub (pinned to 8600b91f42)