uutils/coreutils · error · std::io::Error

head-error-writing-stdout

Error message

head-error-writing-stdout

What it means

`head` wraps any `io::Error` raised while writing parsed content to stdout, replacing the message with a localized `head-error-writing-stdout` that embeds the stripped errno text, while preserving the original error kind. It tells the user the failure happened at the output stage, not while reading input.

Solutions

  1. Check the shell pipeline: ensure the consumer of head's output is alive and reading
  2. Redirect to a writable destination (`df -h` to confirm free space; test with `> /tmp/out`)
  3. If SIGPIPE from early reader exit is expected, ignore it (exit 141 is normal)
  4. Run without redirection to verify stdout itself works

Example fix

// before
head -n 100 log.txt > /dev/full
// after
head -n 100 log.txt > output.txt
Defensive patterns

Strategy: try-catch

Validate before calling

[ -w "$(dirname "$out")" ] && df -h --output=avail "$out" | awk 'NR==2 && $1+0>0' || echo 'stdout target not writable'

Type guard

fn is_stdout_writable() -> bool {
    use std::io::Write;
    std::io::stdout().flush().is_ok()
}

Try / catch

match result {
    Err(e) if e.kind() == ErrorKind::BrokenPipe => { /* consumer exited early: ignore */ }
    Err(e) => { eprintln!("head: error writing stdout: {}"); exit(1); }
    Ok(()) => {}
}

Prevention

When it happens

Trigger: Writing the first N lines/bytes to stdout fails, e.g. `head big.txt | head -c 100 > /dev/full`, a closed pipe (`head f | true`), or a full disk / EPIPE on the terminal.

Common situations: Piping into `head`-terminated pipelines where the downstream reader exits early (SIGPIPE handling), redirecting to `/dev/full`, or writing to a read-only/closed descriptor.

Related errors


AI-assisted analysis of uutils/coreutils@92995feca2 (2026-09-20). Data as JSON: /api/errors/cdc00921cc789ffd. Report an issue: GitHub.

Appendix: source

Thrown at src/uu/head/src/head.rs:209

            verbose: matches.get_flag(options::VERBOSE),
            line_ending: LineEnding::from_zero_flag(matches.get_flag(options::ZERO)),
            presume_input_pipe: matches.get_flag(options::PRESUME_INPUT_PIPE),
            mode: Mode::from(matches)?,
            // #[allow(clippy::unwrap_used, reason = "clap provides '-' by default")] <https://github.com/rust-lang/rust/issues/15701>
            files: matches
                .get_many::<OsString>(options::FILES)
                .unwrap()
                .cloned()
                .collect(),
        };

        Ok(options)
    }
}

#[inline]
fn wrap_in_stdout_error(err: io::Error) -> io::Error {
    io::Error::new(
        err.kind(),
        translate!("head-error-writing-stdout", "err" => uucore::error::strip_errno(&err)),
    )
}

// zero-copy fast-path
#[cfg(any(target_os = "linux", target_os = "android"))]
fn print_n_bytes(input: impl AsFd, n: u64) -> io::Result<u64> {
    let out = io::stdout();
    uucore::pipes::send_n_bytes(input, &out, n).map_err(wrap_in_stdout_error)
}

#[cfg(not(any(target_os = "linux", target_os = "android")))]
fn print_n_bytes(input: impl Read, n: u64) -> io::Result<u64> {
    // Read the first `n` bytes from the `input` reader.
    let mut reader = input.take(n);

    // Write those bytes to `stdout`.

View on GitHub (pinned to 92995feca2)