nikivdev/code · error

plan body is empty

Error message

plan body is empty

What it means

This function reads a plan document from stdin (io::stdin().read_to_string) and, after successfully reading it, checks that the trimmed body is non-empty. An empty or whitespace-only stdin input produces this error, because a plan file with no content would be invalid. Note the preceding read is wrapped with context 'failed to read plan body from stdin', so this bail specifically means stdin was readable but blank.

Source

Thrown at src/codex_runtime.rs:1244

        .ok()
        .or_else(|| env::var("FLOW_CODEX_RUNTIME_STATE").ok())?;
    let path = PathBuf::from(raw_path);
    let raw = fs::read(path).ok()?;
    serde_json::from_slice::<CodexRuntimeState>(&raw).ok()
}

pub fn write_plan_from_stdin(
    title: Option<&str>,
    stem: Option<&str>,
    dir: Option<&str>,
    source_session: Option<&str>,
) -> Result<PathBuf> {
    let mut body = String::new();
    io::stdin()
        .read_to_string(&mut body)
        .context("failed to read plan body from stdin")?;
    if body.trim().is_empty() {
        bail!("plan body is empty");
    }

    let root = resolve_plan_root(dir);
    fs::create_dir_all(&root)?;

    let resolved_title = title
        .map(str::trim)
        .filter(|value| !value.is_empty())
        .map(ToOwned::to_owned)
        .unwrap_or_else(|| derive_plan_title(&body));
    let mut resolved_stem = stem
        .map(str::trim)
        .filter(|value| !value.is_empty())
        .map(ToOwned::to_owned)
        .unwrap_or_else(|| slugify(&resolved_title));
    if !resolved_stem.ends_with("-plan") {
        resolved_stem.push_str("-plan");
    }

View on GitHub (pinned to a747e741ae)

Solutions

  1. Pipe the actual plan content into the command, e.g. `cat plan.md | tool plan --title "..."`.
  2. Check the upstream command that feeds stdin — it may be failing or producing empty output.
  3. In CI/cron, ensure stdin is connected to a file with content rather than /dev/null.
  4. Add a pre-check in your pipeline: fail early if the generated plan body is empty before invoking the tool.

Example fix

// before
tool plan --title "Refactor" < /dev/null
// after
tool plan --title "Refactor" < ./plans/refactor.md
Defensive patterns

Strategy: validation

Validate before calling

let mut body = String::new();
io::stdin().read_to_string(&mut body)?;
if body.trim().is_empty() {
    return Err("refusing to call: plan body from stdin is empty".into());
}

Type guard

fn has_content(s: &str) -> bool { !s.trim().is_empty() }

Try / catch

match write_plan_from_stdin(dir, title) {
    Err(e) if e.to_string().contains("plan body is empty") => {
        eprintln!("stdin was empty — check the upstream command/file feeding the pipe.");
    }
    other => other?,
}

Prevention

When it happens

Trigger: Invoking the plan-creation routine with stdin closed, redirected from an empty file (`< /dev/null` or `< empty.txt`), or piped from a command that produced no output (`echo -n '' | tool plan ...`).

Common situations: Forgetting to pipe the plan content and running the command in a non-interactive context where stdin is /dev/null (CI jobs, cron); an upstream generator failing silently and emitting nothing; heredocs left empty; paste failures in scripted pipelines.

Related errors


AI-assisted analysis of nikivdev/code@a747e741ae (2026-09-01). Data as JSON: /api/errors/6cfaf3b61e59dca2. Report an issue: GitHub.