{"record":{"id":"a099c3bc357af5a9","repo":"facebook/flow","slug":"failed-to-flush-spinner-status-a099c3","errorCode":null,"errorMessage":"failed to flush spinner status","messagePattern":"failed to flush spinner status","errorType":"panic","errorClass":null,"httpStatus":null,"severity":"error","filePath":"rust_port/crates/flow_commands_connect/src/command_connect.rs","lineNumber":221,"sourceCode":"                BusyReason::FailOnInit(..) => {\n                    \"is still initializing and the client used --retry-if-init false\"\n                }\n            };\n            if !env.quiet {\n                eprint!(\n                    \"The flow server {} ({} {} remaining): {}\",\n                    busy_reason_str,\n                    retries.retries_remaining,\n                    if retries.retries_remaining == 1 {\n                        \"retry\"\n                    } else {\n                        \"retries\"\n                    },\n                    flow_utils_tty::spinner(false),\n                );\n                std::io::stderr()\n                    .flush()\n                    .expect(\"failed to flush spinner status\");\n            }\n            consume_retry(retries);\n            connect_rec(env, client_handshake, retries)\n        }\n\n        Err(CCSError::BuildIdMismatch(MismatchBehavior::ServerExited)) => {\n            let msg = \"The flow server's version didn't match the client's, so it exited.\";\n            if env.autostart {\n                if !env.quiet {\n                    eprintln!(\"{}\\nGoing to launch a new one.\\n\", msg);\n                }\n                // Don't decrement retries -- the server is definitely not running,\n                // so the next time round will hit Server_missing above, *but*\n                // before that will actually start the server -- we need to make\n                // sure that happens.\n                connect_rec(env, client_handshake, retries)\n            } else {\n                let msg = format!(\"\\n{}\", msg);","sourceCodeStart":203,"sourceCodeEnd":239,"githubUrl":"https://github.com/facebook/flow/blob/f88ac94bcf6992f5d5a158854d94613ebb92c6e6/rust_port/crates/flow_commands_connect/src/command_connect.rs#L203-L239","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Redirect stderr to a file or keep the pipe reader alive for the whole command.","Retry when the server is less loaded (or raise the client limit on the server) so the busy-retry loop is short.","Code fix: make status-output flushes non-fatal (`let _ = ...flush()`)."],"exampleFix":"// before\nstd::io::stderr().flush().expect(\"failed to flush spinner status\");\n\n// after\nlet _ = std::io::stderr().flush(); // status line; broken stderr pipe must not kill the client","handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"eprint!(\"The flow server {} ...\", ...);\nlet _ = std::io::stderr().flush(); // status output; EPIPE must not kill retry loop","preventionTips":["Redirect stderr to a file when scripting flow commands that may retry a busy server.","Reduce server client load or retry later so busy-retry loops stay short.","Treat all spinner/status writes to stderr as best-effort in wrapper code."],"tags":["stderr","broken-pipe","spinner","server-busy"],"backgroundTag":"broken-pipe","analyzedSha":"f88ac94bcf6992f5d5a158854d94613ebb92c6e6","analyzedAt":"2026-08-20T10:41:37.992Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}