rust-lang/mdBook · error

Command string was empty

Error message

Command string was empty

What it means

mdbook's compose_command builds a std::process::Command for an external renderer or preprocessor by shell-splitting the command string with Shlex. If the configured command string is empty (or contains only whitespace), there is no executable to run, so the library bails with this error instead of spawning an invalid process. It is a configuration validation error surfaced as a Result::Err.

Source

Thrown at crates/mdbook-driver/src/lib.rs:86

pub mod builtin_renderers;
pub mod init;
mod load;
mod mdbook;

use anyhow::{Context, Result, bail};
pub use mdbook::MDBook;
pub use mdbook_core::{book, config, errors};
use shlex::Shlex;
use std::path::{Path, PathBuf};
use std::process::Command;
use tracing::{error, warn};

/// Creates a [`Command`] for command renderers and preprocessors.
fn compose_command(cmd: &str, root: &Path) -> Result<Command> {
    let mut words = Shlex::new(cmd);
    let exe = match words.next() {
        Some(e) => PathBuf::from(e),
        None => bail!("Command string was empty"),
    };

    let exe = if exe.components().count() == 1 {
        // Search PATH for the executable.
        exe
    } else {
        // Relative path is relative to book root.
        root.join(&exe)
    };

    let mut cmd = Command::new(exe);

    for arg in words {
        cmd.arg(arg);
    }

    Ok(cmd)
}

View on GitHub (pinned to dc21064fc2)

Solutions

  1. Set a non-empty command for the renderer/preprocessor in book.toml, e.g. command = "mdbook-foo".
  2. If the command comes from an environment variable, ensure the variable is exported with the actual executable path before running mdbook.
  3. Remove the empty command key entirely so mdbook uses its default lookup for the preprocessor/renderer.
  4. If writing a plugin, validate the command string is non-empty (Shlex-parses to at least one token) before registering.

Example fix

// before (book.toml)
[preprocessor.links]
command = ""

// after (book.toml)
[preprocessor.links]
command = "mdbook-links"
Defensive patterns

Strategy: validation

Validate before calling

let cmd = config.get("output.my-renderer.command").unwrap_or("");
if cmd.trim().is_empty() {
    return Err(anyhow!("renderer command must be a non-empty string"));
}

Type guard

fn has_command(cmd: &Option<String>) -> bool {
    cmd.as_deref().map(|c| !c.trim().is_empty()).unwrap_or(false)
}

Prevention

When it happens

Trigger: Setting a renderer or preprocessor command in book.toml to an empty string (e.g. `output.xyz.command = ""` or `[preprocessor.foo]\ncommand = ""`), or passing an empty/whitespace-only cmd to any code path that calls compose_command.

Common situations: Empty command inherited from an environment variable interpolation (e.g. MY_CMD="" mdbook build), a config templating step that dropped the value, or hand-edited book.toml where the command value was accidentally deleted but the key kept.

Related errors


AI-assisted analysis of rust-lang/mdBook@dc21064fc2 (2026-09-01). Data as JSON: /api/errors/3a125f4bcb57625c. Report an issue: GitHub.