Hmbown/CodeWhale · error

install source must not be empty

Error message

install source must not be empty

What it means

PluginInstallSource::parse validates the install spec string before routing it to a remote or local source. An empty (or whitespace-only) spec cannot identify any install source, so parsing fails immediately with this error.

Solutions

  1. Provide a non-empty install source: `github:owner/repo`, an `https://…` URL, or `path:<dir>`
  2. Fix the config/CLI value that expands to an empty string
  3. Guard the call site: trim and check `!spec.is_empty()` before invoking parse

Example fix

// before
let src = PluginInstallSource::parse(&spec)?;
// after
let trimmed = spec.trim();
anyhow::ensure!(!trimmed.is_empty(), "plugin install source is required");
let src = PluginInstallSource::parse(trimmed)?;
Defensive patterns

Strategy: validation

Validate before calling

fn install_spec_ok(spec: &str) -> bool { !spec.trim().is_empty() }

Try / catch

match PluginInstallSource::parse(spec) { Err(e) if e.to_string().contains("must not be empty") => eprintln!("provide github:owner/repo, https://… or path:<dir>"), other => other?, }

Prevention

When it happens

Trigger: Calling PluginInstallSource::parse("") or parse(" "), e.g. an unset plugin name/source field in a config file, an empty CLI argument, or a plugin entry missing its `source` key.

Common situations: Config TOML/JSON with `source = ""`; shell variable expansion producing an empty string (`install "$PLUGIN_SRC"` with PLUGIN_SRC unset); UI form submitted without choosing a source.

Understand the failure class

Background: "must not be empty", "cannot be empty" — required-field validation errors across open-source libraries — this error's family across 41 libraries.

Related errors


AI-assisted analysis of Hmbown/CodeWhale@73e0f67d83 (2026-09-22). Data as JSON: /api/errors/43029c9faee49315. Report an issue: GitHub.

Appendix: source

Thrown at crates/tui/src/plugins/install/mod.rs:110

    /// Local bundle directory (copied, never executed). Parsed from a plain
    /// path or an explicit `path:<dir>` spec (the marker round-trip form).
    LocalPath(PathBuf),
    /// `github:owner/repo` or a direct `http(s)://…` tarball URL, downloaded
    /// through the shared skill-install machinery. There is no registry
    /// index in v1.
    Remote(InstallSource),
}

impl PluginInstallSource {
    /// Parse a user-supplied spec.
    ///
    /// * `github:owner/repo`, `https://…` → [`PluginInstallSource::Remote`]
    ///   (via [`InstallSource::parse`]; registry names are unreachable here)
    /// * `path:<dir>` or any other value → [`PluginInstallSource::LocalPath`]
    pub fn parse(spec: &str) -> Result<Self> {
        let trimmed = spec.trim();
        if trimmed.is_empty() {
            bail!("install source must not be empty");
        }
        if let Some(path) = trimmed.strip_prefix("path:") {
            return Self::local(path);
        }
        if trimmed.starts_with("github:")
            || trimmed.starts_with("https://")
            || trimmed.starts_with("http://")
        {
            let source = InstallSource::parse(trimmed)?;
            return match source {
                InstallSource::GitHubRepo(_) | InstallSource::DirectUrl(_) => {
                    remote_bundle_path(&source)?;
                    Ok(Self::Remote(source))
                }
                InstallSource::Registry(_) => {
                    unreachable!("prefixed specs never parse as a registry name")
                }
            };

View on GitHub (pinned to 73e0f67d83)