facebook/flow · error

failed to write spinner status

Error message

failed to write spinner status

What it means

Raised by print_status in flow_commands_connect when writing the spinner status line (`message: <spinner>`) to stderr fails. It only takes this path when show_progress is on and stderr is a terminal, so write!(stderr, ...) failing means the terminal write itself errored (EPIPE, closed descriptor, I/O error). The function panics via expect because status output is assumed to always be writable.

Source

Thrown at rust_port/crates/flow_commands_connect/src/command_connect.rs:50

    pub ignore_version: bool,
    #[allow(dead_code)]
    pub emoji: bool,
    pub quiet: bool,
    pub show_progress: bool,
    pub flowconfig_name: &'a str,
    pub rerun_on_mismatch: bool,
}

fn print_status(env: &Env<'_>, message: &str) {
    if env.quiet {
        return;
    }

    let stderr = std::io::stderr();
    let mut stderr = stderr.lock();
    if env.show_progress && stderr.is_terminal() {
        write!(stderr, "{}: {}", message, flow_utils_tty::spinner(false))
            .expect("failed to write spinner status");
    } else {
        writeln!(stderr, "{}", message).expect("failed to write status");
    }
    stderr.flush().expect("failed to flush status");
}

fn arg(name: &str, value: Option<&str>, arr: &mut Vec<String>) {
    if let Some(value) = value {
        arr.push(name.to_string());
        arr.push(value.to_string());
    }
}

fn flag(name: &str, value: bool, arr: &mut Vec<String>) {
    if value {
        arr.push(name.to_string());
    }
}

View on GitHub (pinned to 5c86586199)

Solutions

  1. Run with a healthy stderr: rerun in a live terminal, or redirect stderr to a file (`2>flow.log`).
  2. Pass --quiet (or disable progress) so print_status takes the plain writeln path or skips output.
  3. Avoid piping stderr to consumers that exit early; if piping, use `tail -f` on a file instead.
  4. If the library is yours, replace the expect with best-effort error handling for stderr writes.

Example fix

// before
write!(stderr, "{}: {}", message, flow_utils_tty::spinner(false))
    .expect("failed to write spinner status");
// after
if write!(stderr, "{}: {}", message, flow_utils_tty::spinner(false)).is_err() {
    return; // status output is best-effort
}
Defensive patterns

Strategy: fallback

Validate before calling

use std::io::IsTerminal;
fn status_output_ok(show_progress: bool) -> bool {
    std::io::stderr().is_terminal() || std::io::stderr().flush().is_ok()
}

Type guard

fn stderr_alive() -> bool {
    use std::io::Write;
    let mut e = std::io::stderr();
    e.write_all(b"").and_then(|_| e.flush()).is_ok()
}

Try / catch

match std::panic::catch_unwind(|| run_connect_command()) {
    Ok(v) => v,
    Err(_) => eprintln!("status write failed; rerun with --quiet and stderr to a file"),
}

Prevention

When it happens

Trigger: connect_rec or handle_missing_server calls print_status while progress output is enabled and stderr is a TTY whose write fails — e.g. the terminal emulator closed, stderr redirected to a pipe whose reader exited, or a device I/O error.

Common situations: Running `flow connect`-style commands in an IDE terminal that gets closed mid-run; stderr piped into a process like `less` or `head` that exits before the command finishes; SSH session dropped with stderr still attached to the dead pty.

Related errors


AI-assisted analysis of facebook/flow@5c86586199 (2026-09-08). Data as JSON: /api/errors/6edc73f8a73e25e8. Report an issue: GitHub.