Kuberwastaken/claurst · error

Plugin name ' ' cannot contain spaces. Use kebab-case.

Error message

Plugin name '{}' cannot contain spaces. Use kebab-case.

What it means

Plugin manifest names must be kebab-case; the validator rejects any name containing a space character. This keeps plugin names valid as identifiers on disk, in registries, and in CLI references, matching the TS schema checks.

Solutions

  1. Replace spaces in the name with hyphens, e.g. "My Plugin" -> "my-plugin"
  2. Lowercase the name to conform to kebab-case conventions
  3. Keep the human-readable title in a separate display-name field if the schema supports one

Example fix

// before (plugin.toml)
name = "My Plugin"
// after
name = "my-plugin"
Defensive patterns

Strategy: validation

Validate before calling

fn kebab_case(name: &str) -> bool {
    !name.is_empty() && !name.contains(' ') && name.chars().all(|c| c.is_ascii_lowercase() || c == '-' || c.is_ascii_digit())
}

Try / catch

match PluginManifest::from_toml(&toml_str) {
    Err(e) if e.to_string().contains("cannot contain spaces") => { /* normalize name to kebab-case */ }
    other => other?,
}

Prevention

When it happens

Trigger: PluginManifest::validate (via from_toml) sees a name value containing at least one ' ' character, e.g. name = "My Plugin".

Common situations: Author wrote a display name instead of an identifier in plugin.toml; renaming a plugin to a human-friendly label; copied name from a README heading.

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


AI-assisted analysis of Kuberwastaken/claurst@b0637c97ec (2026-09-10). Data as JSON: /api/errors/6b580788fb81719e. Report an issue: GitHub.

Appendix: source

Thrown at src-rust/crates/plugins/src/manifest.rs:347

        manifest.validate()?;
        Ok(manifest)
    }

    /// Parse a manifest from TOML bytes (plugin.toml).
    pub fn from_toml(bytes: &[u8]) -> anyhow::Result<Self> {
        let s = std::str::from_utf8(bytes)?;
        let manifest: PluginManifest = toml::from_str(s)?;
        manifest.validate()?;
        Ok(manifest)
    }

    /// Basic validation matching the TS schema checks.
    pub fn validate(&self) -> anyhow::Result<()> {
        if self.name.is_empty() {
            anyhow::bail!("Plugin name cannot be empty");
        }
        if self.name.contains(' ') {
            anyhow::bail!(
                "Plugin name '{}' cannot contain spaces. Use kebab-case.",
                self.name
            );
        }
        Ok(())
    }
}

/// Normalise the raw JSON value so that both camelCase and snake_case
/// variants of known fields work, and so `mcpServers` (object mapping) is
/// converted to a `Vec<PluginMcpServer>`.
fn normalize_manifest_json(mut v: serde_json::Value) -> serde_json::Value {
    let obj = match v.as_object_mut() {
        Some(o) => o,
        None => return v,
    };

    // Promote `mcpServers` (TS camelCase object) → `mcp_servers` (array).

View on GitHub (pinned to b0637c97ec)