AlexsJones/llmfit · error
CSV serialization failed
Error message
CSV serialization failed
What it means
Panic from .expect("CSV serialization failed") on csv::Writer::serialize(CsvFitRow {...}) in display_csv_fits (display.rs:957-989), which backs `llmfit fit --csv` (main.rs:1131/1526). CsvFitRow is flat (strings, f64s, bools, Options), so CSV field serialization itself cannot fail; the realistic failure is the underlying std::io::stdout() writer returning an IO error — overwhelmingly EPIPE when the downstream pipe consumer (head, grep -q, awk with exit, a closed pager) closes stdout before all rows are written. Because Rust ignores SIGPIPE by default, the EPIPE surfaces as a csv::Error and the expect turns it into a panic.
Source
Thrown at llmfit-tui/src/display.rs:1039
score: round1(fit.score),
score_quality: round1(fit.score_components.quality),
score_speed: round1(fit.score_components.speed),
score_fit: round1(fit.score_components.fit),
score_context: round1(fit.score_components.context),
estimated_tps: round1(fit.estimated_tps),
memory_required_gb: round2(fit.memory_required_gb),
memory_available_gb: round2(fit.memory_available_gb),
utilization_pct: round1(fit.utilization_pct),
disk_size_gb: round2(fit.model.estimate_disk_gb(&fit.best_quant)),
best_quant: fit.best_quant.clone(),
runtime: fit.runtime.label().to_string(),
use_case: fit.use_case.label().to_string(),
release_date: fit.model.release_date.clone(),
license: fit.model.license.clone(),
is_moe: fit.model.is_moe,
installed: fit.installed,
})
.expect("CSV serialization failed");
}
writer.flush().expect("CSV flush failed");
}
#[cfg(test)]
mod tests {
use super::*;
use llmfit_core::fit::{FitLevel, InferenceRuntime, ScoreComponents};
use llmfit_core::models::{Capability, GgufSource, ModelFormat, UseCase};
fn mock_fit(run_mode: RunMode, use_case: UseCase, model_use_case: &str) -> ModelFit {
ModelFit {
model: LlmModel {
name: "test/model-7b".to_string(),
provider: "test".to_string(),
parameter_count: "7B".to_string(),
parameters_raw: None,View on GitHub (pinned to 8f16394d74)
Solutions
- Redirect to a file instead of piping: `llmfit fit --csv > models.csv`, then sample the file with head afterwards.
- Use llmfit's own row limiting to avoid early-exit pipes: `llmfit fit --csv -n 5` prints only 5 rows so `| head` is unnecessary.
- If you must pipe, let the consumer read everything (e.g. `grep pattern` without -q) or use `set -o pipefail`-aware wrappers that tolerate exit code 141.
- Check disk space and mount health when stdout is redirected to a file.
- As a maintainer, handle csv::ErrorKind::Io(BrokenPipe) with std::process::exit(141) instead of expect, and consider restoring default SIGPIPE handling in main.
Example fix
// before
writer.serialize(row).expect("CSV serialization failed");
// after
if let Err(e) = writer.serialize(row) {
if let csv::ErrorKind::Io(io_err) = e.kind() {
if io_err.kind() == std::io::ErrorKind::BrokenPipe {
std::process::exit(141); // consumer closed the pipe; not an error
}
}
eprintln!("error: CSV serialization failed: {e}");
std::process::exit(1);
} Defensive patterns
Strategy: fallback
Validate before calling
# Shell-level: avoid the early-exit pipe that triggers EPIPE — write to a file instead. llmfit fit --csv > /tmp/llmfit-models.csv # then: head -n 5 /tmp/llmfit-models.csv # Or limit rows at the source so no pipe truncation is needed: # llmfit fit --csv -n 5
Try / catch
// Keep the panic off the user's screen and map it to the conventional SIGPIPE exit code.
let result = std::panic::catch_unwind(std::panic::AssertUnwindSafe(|| {
display::display_csv_fits(&fits);
}));
if result.is_err() {
// Distinguish broken pipe from real failures before reporting.
eprintln!("error: CSV output aborted (broken pipe or write failure)");
std::process::exit(141);
} Prevention
- Never pipe `llmfit fit --csv` into head/grep -q/awk-exit; redirect to a file and post-process it.
- Use the -n limit flag to bound rows at the source instead of truncating the pipe.
- When scripting, check free space on the redirect target volume before long CSV runs.
- Maintainers: exit(141) on csv::ErrorKind::Io(BrokenPipe) instead of expect, and consider restoring default SIGPIPE disposition in main().
When it happens
Trigger: `llmfit fit --csv | head -n 5`, `llmfit fit --csv | grep -q some-model`, or piping CSV into any command that exits before consuming all ~33+ model rows; also redirecting stdout to a full disk or a revoked network mount.
Common situations: Shell one-liners sampling the CSV with head/take-style tools; CI scripts that pipe the CSV into a matcher with -m1; running the command in a terminal whose pager is killed mid-output; writing to a file on a full filesystem.
Related errors
- CSV flush failed
- embedded use_case_benchmarks.json is invalid
- CARGO_MANIFEST_DIR
- OUT_DIR
- failed to write generated web_assets.rs
AI-assisted analysis of AlexsJones/llmfit@8f16394d74 (2026-08-17).
Data as JSON: /api/errors/35e24dc3a7dda7c2.
Report an issue: GitHub.