ultraworkers/claw-code · error
rustyline editor should initialize
Error message
rustyline editor should initialize
What it means
LineEditor::new in the claw CLI (rust/crates/rusty-claude-cli/src/input.rs:114) calls Editor::with_config(config).expect("rustyline editor should initialize"). rustyline's Editor construction opens and configures the terminal (termios on Unix, console mode on Windows); when that fails it returns a ReadlineError (typically Io or Errno), and this .expect turns it into a panic during REPL startup. The usual cause is that stdin/stdout is not an interactive terminal or the terminal cannot be configured (TERM unset/dumb, no /dev/tty).
Source
Thrown at rust/crates/rusty-claude-cli/src/input.rs:114
}
impl Validator for SlashCommandHelper {}
impl Helper for SlashCommandHelper {}
pub struct LineEditor {
prompt: String,
editor: Editor<SlashCommandHelper, DefaultHistory>,
}
impl LineEditor {
#[must_use]
pub fn new(prompt: impl Into<String>, completions: Vec<String>) -> Self {
let config = Config::builder()
.completion_type(CompletionType::List)
.edit_mode(EditMode::Emacs)
.build();
let mut editor = Editor::<SlashCommandHelper, DefaultHistory>::with_config(config)
.expect("rustyline editor should initialize");
editor.set_helper(Some(SlashCommandHelper::new(completions)));
editor.bind_sequence(KeyEvent(KeyCode::Char('J'), Modifiers::CTRL), Cmd::Newline);
editor.bind_sequence(KeyEvent(KeyCode::Enter, Modifiers::SHIFT), Cmd::Newline);
Self {
prompt: prompt.into(),
editor,
}
}
pub fn push_history(&mut self, entry: impl Into<String>) {
let entry = entry.into();
if entry.trim().is_empty() {
return;
}
let _ = self.editor.add_history_entry(entry);
}View on GitHub (pinned to 08106b0c37)
Solutions
- Run the REPL from an actual interactive terminal (attach a pty: `docker exec -it`, `ssh -t`, a real terminal emulator).
- Use the CLI's non-interactive mode instead of the REPL — pass the prompt as an argument / print mode rather than piping stdin, so LineEditor is never constructed.
- Set a valid TERM (e.g. `export TERM=xterm-256color`) when TERM is unset or 'dumb'.
- If you embed rustyline yourself, don't .expect(): match the Err(ReadlineError) and fall back to plain stdin line reading.
- Verify with a tty check (`[ -t 0 ]` / std::io::IsTerminal) before launching the interactive loop and fall back to non-interactive input.
Example fix
// before (input.rs)
let mut editor = Editor::<SlashCommandHelper, DefaultHistory>::with_config(config)
.expect("rustyline editor should initialize");
// after: fall back to plain stdin when the terminal cannot be initialized
let editor = match Editor::<SlashCommandHelper, DefaultHistory>::with_config(config) {
Ok(mut editor) => {
editor.set_helper(Some(SlashCommandHelper::new(completions)));
LineEditorKind::Rustyline(editor)
}
Err(err) => {
eprintln!("warning: interactive editor unavailable ({err}); using plain input");
LineEditorKind::Plain
}
}; Defensive patterns
Strategy: validation
Validate before calling
// before starting the interactive REPL, verify a usable terminal
use std::io::IsTerminal;
fn can_run_repl() -> bool {
std::io::stdin().is_terminal()
&& std::io::stdout().is_terminal()
&& std::env::var("TERM").map(|t| !t.is_empty() && t != "dumb").unwrap_or(false)
}
if !can_run_repl() {
eprintln!("stdin/stdout is not an interactive terminal; use non-interactive mode");
std::process::exit(2);
} Try / catch
// if you embed rustyline directly, avoid .expect() on Editor creation
match Editor::<H, DefaultHistory>::with_config(config) {
Ok(editor) => run_repl(editor),
Err(rustyline::error::ReadlineError::Io(e)) => fall_back_to_plain_stdin(e),
Err(rustyline::error::ReadlineError::Errno(e)) => fall_back_to_plain_stdin(e),
Err(other) => return Err(other.into()),
} Prevention
- Never pipe stdin into the REPL (`claw < file`); use the CLI's one-shot/print mode for scripted input.
- Allocate a TTY in containers and remote sessions (`docker run -it`, `ssh -t`).
- Ensure TERM is set to a real terminal type (xterm-256color) and never 'dumb' when launching interactively.
- Gate REPL startup on `[ -t 0 ]` / std::io::IsTerminal checks and fall back to non-interactive input.
When it happens
Trigger: Starting the claw interactive REPL when stdin is not a TTY — e.g. `cat file | claw`, `claw < input.txt`, running under CI/ssh without a pty, or inside `docker run` without `-t`. Also triggered by TERM=dumb or an unset TERM on Unix, or when /dev/tty cannot be opened, since rustyline must put the terminal into raw mode at Editor creation.
Common situations: Piping input into the CLI in scripts or CI and accidentally entering REPL mode; docker/Kubernetes containers without TTY allocation; cron jobs or service wrappers invoking the binary; broken TERM after su/sudo or in minimal distro images; Windows consoles with unavailable console APIs.
Related errors
- missing_flag_value: missing value for --cwd. Usage: --cwd <p
- cron registry lock poisoned
- worker registry lock poisoned
- HOME is not set (on Windows, set USERPROFILE or HOME, or use
- credentials file must contain a JSON object
AI-assisted analysis of ultraworkers/claw-code@08106b0c37 (2026-08-18).
Data as JSON: /api/errors/401584919dfd279a.
Report an issue: GitHub.