herdrdev/herdr · error · io::Error

build command must not be empty

Error message

build command must not be empty

What it means

When running a managed plugin's build command, the runner requires a non-empty command vector; command.first() returning None (src/cli/plugin.rs:1310) yields PluginBuildFailureKind::Start with io::ErrorKind::InvalidInput 'build command must not be empty'. This means the plugin manifest or resolved build configuration produced an empty argv.

Solutions

  1. Fix the plugin manifest so its build command is a non-empty argv, e.g. build = ["cargo", "build", "--release"]
  2. If the plugin should not be built, remove/skip the build configuration instead of leaving it empty
  3. Reinstall the plugin from its source so a well-formed manifest is regenerated

Example fix

# before (plugin manifest)
build = []

# after
build = ["cargo", "build", "--release"]
Defensive patterns

Strategy: validation

Validate before calling

if plugin_manifest.build.is_empty() {
    return Err("plugin build command must not be empty".into());
}
run_plugin_build(&manifest)?;

Try / catch

match run_plugin_build(...) {
    Err(e) if matches!(e.kind_str(), "build command must not be empty") => fix_manifest(),
    r => r,
}

Prevention

When it happens

Trigger: Building a managed plugin whose manifest declares an empty build command array (e.g. build = [] or build = "" split into zero tokens), so the spawned argv has no program element.

Common situations: Hand-edited plugin manifests, a plugin spec that omits or blank-initializes the build field while still marking the plugin as needing builds, or templating bugs that strip the command.

Related errors


AI-assisted analysis of herdrdev/herdr@f457cff4f2 (2026-08-28). Data as JSON: /api/errors/d7639cd95c779f88. Report an issue: GitHub.

Appendix: source

Thrown at src/cli/plugin.rs:1310

    }
    Err(io::Error::other(
        "plugin build changed herdr-plugin.toml after install preview; aborting install",
    ))
}

fn run_plugin_build_command(
    plugin_id: &str,
    build_index: usize,
    build_total: usize,
    cwd: &Path,
    command: &[String],
) -> Result<(), Box<PluginBuildFailure>> {
    let context = plugin_build_context(plugin_id, build_index, build_total, cwd, command);
    let Some(program) = command.first() else {
        return Err(Box::new(PluginBuildFailure {
            context,
            kind: PluginBuildFailureKind::Start {
                error: io::Error::new(
                    io::ErrorKind::InvalidInput,
                    "build command must not be empty",
                ),
            },
        }));
    };
    let args = command.iter().skip(1).cloned().collect::<Vec<_>>();
    let mut child = crate::plugin_command::command_for_argv_in_dir(program, &args, cwd);
    child
        .stdin(Stdio::null())
        .stdout(Stdio::piped())
        .stderr(Stdio::piped());
    scrub_herdr_runtime_env(&mut child);

    let mut child = match child.spawn() {
        Ok(child) => child,
        Err(err) => {
            return Err(Box::new(PluginBuildFailure {

View on GitHub (pinned to f457cff4f2)