facebook/flow · error

failed to flush spinner status

Error message

failed to flush spinner status

What it means

In the ServerBusy retry path the client prints `"The flow server ... (N retries remaining)"` with a spinner frame and flushes stderr via `expect("failed to flush spinner status")`. The panic fires when flushing stderr errors — the stderr pipe's reader has closed (EPIPE) or fd 2 is invalid. It surfaces specifically after a `TooManyClients`/busy response, i.e., during long retry loops where a piped reader is likely to have exited.

Source

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

                BusyReason::FailOnInit(..) => {
                    "is still initializing and the client used --retry-if-init false"
                }
            };
            if !env.quiet {
                eprint!(
                    "The flow server {} ({} {} remaining): {}",
                    busy_reason_str,
                    retries.retries_remaining,
                    if retries.retries_remaining == 1 {
                        "retry"
                    } else {
                        "retries"
                    },
                    flow_utils_tty::spinner(false),
                );
                std::io::stderr()
                    .flush()
                    .expect("failed to flush spinner status");
            }
            consume_retry(retries);
            connect_rec(env, client_handshake, retries)
        }

        Err(CCSError::BuildIdMismatch(MismatchBehavior::ServerExited)) => {
            let msg = "The flow server's version didn't match the client's, so it exited.";
            if env.autostart {
                if !env.quiet {
                    eprintln!("{}\nGoing to launch a new one.\n", msg);
                }
                // Don't decrement retries -- the server is definitely not running,
                // so the next time round will hit Server_missing above, *but*
                // before that will actually start the server -- we need to make
                // sure that happens.
                connect_rec(env, client_handshake, retries)
            } else {
                let msg = format!("\n{}", msg);

View on GitHub (pinned to f88ac94bcf)

Solutions

  1. Redirect stderr to a file or keep the pipe reader alive for the whole command.
  2. Retry when the server is less loaded (or raise the client limit on the server) so the busy-retry loop is short.
  3. Code fix: make status-output flushes non-fatal (`let _ = ...flush()`).

Example fix

// before
std::io::stderr().flush().expect("failed to flush spinner status");

// after
let _ = std::io::stderr().flush(); // status line; broken stderr pipe must not kill the client
Defensive patterns

Strategy: try-catch

Try / catch

eprint!("The flow server {} ...", ...);
let _ = std::io::stderr().flush(); // status output; EPIPE must not kill retry loop

Prevention

When it happens

Trigger: Piping stderr to a short-lived reader (`2>&1 | head`) that exits while the client is retrying a busy server; a log collector dying mid-retry; fd 2 closed by the launching wrapper.

Common situations: Scripts piping flow output into head/grep; CI log forwarders that terminate early; servers under heavy client load making the retry loop long enough to outlive the reader.

Related errors


AI-assisted analysis of facebook/flow@f88ac94bcf (2026-08-20). Data as JSON: /api/errors/a099c3bc357af5a9. Report an issue: GitHub.