zeroclaw-labs/zeroclaw · error · anyhow::Error
--description is required when stdin is not a TTY
Error message
--description is required when stdin is not a TTY
What it means
Thrown by prompt_for_description in the skill-creation CLI flow (src/skills/mod.rs). When no usable --description was given and stdin is not a TTY, the interactive dialoguer prompt cannot be shown, so the command aborts instead of hanging on an invisible prompt. It guarantees scripted or piped invocations always supply an explicit description.
Solutions
- Pass a non-empty --description flag on the command line
- Run the command in a real terminal so the interactive prompt appears
- If wrapping the CLI from another program, allocate a PTY (e.g. script -qc, docker run -t) or always supply the flag
Example fix
# before (stdin piped, fails) echo | zeroclaw skills create deploy-skill # after zeroclaw skills create deploy-skill --description "Deploys the web dashboard"
Defensive patterns
Strategy: validation
Validate before calling
# bash wrapper: always supply --description when not interactive
args=("$@")
if ! [ -t 0 ] && [[ " $* " != *" --description"* ]]; then
args+=(--description "${SKILL_DESCRIPTION:?set SKILL_DESCRIPTION for non-interactive runs}")
fi
zeroclaw skills create "${args[@]}" Prevention
- Always pass --description in scripts, CI, and containers regardless of TTY
- Detect interactivity early (test -t 0 in shell, IsTerminal in Rust) and branch before invoking the CLI
- Never assume a prompt will appear when the command runs under another process
When it happens
Trigger: Running the skill-create command with stdin piped, redirected, or detached from a terminal (CI job, cron, docker exec without -t, another process spawning the CLI) without passing --description; passing --description with only whitespace while stdin is non-interactive.
Common situations: Automation wrappers and CI pipelines that call the CLI without a PTY; running inside containers or IDE task runners where no TTY is allocated; users piping input expecting it to be read as the description.
Understand the failure class
Background: "--flag is required" and "must specify" CLI errors: how missing-required-flag validation works and how to fix it — this error's family across 20 libraries.
Related errors
- cli-secret-needs-tty
- exited with non-zero status
- no editor found; set VISUAL or EDITOR
- No input received from stdin
- cli-bundle-not-configured
AI-assisted analysis of zeroclaw-labs/zeroclaw@88bb9c8533 (2026-08-23).
Data as JSON: /api/errors/ff25a974048b92bd.
Report an issue: GitHub.
Appendix: source
Thrown at src/skills/mod.rs:1227
Ok(())
}
fn prompt_for_description(description: Option<String>) -> Result<String> {
if let Some(d) = description
&& !d.trim().is_empty()
{
return Ok(d);
}
if std::io::IsTerminal::is_terminal(&std::io::stdin()) {
let prompt: String = dialoguer::Input::new()
.with_prompt("Skill description (what it does, when to use it)")
.interact_text()?;
if prompt.trim().is_empty() {
anyhow::bail!("description must not be empty");
}
Ok(prompt)
} else {
anyhow::bail!("--description is required when stdin is not a TTY");
}
}
fn open_in_editor(path: &std::path::Path) -> Result<()> {
let Some(editor) = editor_from_env_or_path() else {
anyhow::bail!("no editor found; set VISUAL or EDITOR");
};
let status = std::process::Command::new(&editor).arg(path).status()?;
if !status.success() {
anyhow::bail!("{editor} exited with non-zero status");
}
Ok(())
}
fn editor_from_env_or_path() -> Option<String> {
std::env::var("VISUAL")
.ok()
.filter(|value| !value.trim().is_empty())View on GitHub (pinned to 88bb9c8533)