can1357/oh-my-pi · error · TacError

failed to write to stdout: {}

Error message

failed to write to stdout: {}

What it means

This tac builtin error (crates/pi-builtins/src/tac.rs, Write) means writing the reversed output to stdout failed. It carries an errno-stripped io::Error (e.g. EPIPE when the downstream reader closed the pipe). This is an output-side failure, not an input problem.

Source

Thrown at crates/pi-builtins/src/tac.rs:39

	pub static BEFORE: &str = "before";
	pub static REGEX: &str = "regex";
	pub static SEPARATOR: &str = "separator";
	pub static FILE: &str = "file";
}

#[derive(Debug, Error)]
enum TacError {
	/// A regular expression given by the user is invalid.
	#[error("invalid regular expression: {0}")]
	InvalidRegex(regex::Error),
	/// An error opening a file for reading.
	#[error("failed to open {} for reading: {}", .0.quote(), strip_errno(.1))]
	Open(OsString, std::io::Error),
	/// An error reading the contents of a file or stdin.
	#[error("{}: read error: {}", .0.maybe_quote(), strip_errno(.1))]
	Read(OsString, std::io::Error),
	/// An error writing the reversed contents of a file or stdin.
	#[error("failed to write to stdout: {}", strip_errno(.0))]
	Write(std::io::Error),
}

fn strip_errno(error: &std::io::Error) -> String {
	let mut message = error.to_string();
	if let Some(position) = message.find(" (os error ") {
		message.truncate(position);
	}
	message
}

/// Parsed `tac` invocation.
pub(crate) struct Tac {
	matches: ArgMatches,
}

matches_parser!(Tac, app);

View on GitHub (pinned to 9690622007)

Solutions

  1. If truncation was intended (e.g. piping to head), ignore the broken-pipe condition; it is expected behavior.
  2. Check free space on the filesystem receiving the output (`df -h`) when redirecting to a file.
  3. Ensure stdout is open in the calling context (avoid running with >&-).
  4. Handle SIGPIPE/BrokenPipe in wrapping scripts so partial output is treated as success.

Example fix

// before: noisy EPIPE
tac huge.log | head -n 10  // failed to write to stdout: Broken pipe

// after: suppress expected broken pipe
tac huge.log | head -n 10 2>/dev/null || true
Defensive patterns

Strategy: try-catch

Type guard

function isWriteError(err) {
  return err instanceof Error && err.message.startsWith('failed to write to stdout: ');
}

Try / catch

try {
  await tac.run([file]);
} catch (err) {
  const msg = String(err);
  if (msg.startsWith('failed to write to stdout:') && /broken pipe|os error 32/i.test(msg)) {
    // downstream closed early (e.g. head): treat as success
    return;
  } else throw err;
}

Prevention

When it happens

Trigger: Downstream consumer exits early: `tac big.txt | head -5` (SIGPIPE/EPIPE); stdout redirected to a full disk or a closed descriptor; output quota exceeded.

Common situations: Piping into head/less/grep that stops reading; shell jobs killed; /tmp or the redirect target filesystem full; detached processes with closed stdout.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/265adeece1be55a8. Report an issue: GitHub.