Skip to content

tail: fix panic on header write error - #12279

Merged
sylvestre merged 1 commit into
uutils:mainfrom
gabrielhnf:fix-tail-header-write-error
Oct 22, 2026
Merged

sylvestre merged 1 commit into
uutils:mainfrom
gabrielhnf:fix-tail-header-write-error

Conversation

@gabrielhnf

@gabrielhnf gabrielhnf commented May 13, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #11993

Problem

When running tail on multiple files and redirecting output to a full device (e.g. /dev/full), uutils panics instead of exiting gracefully:

thread 'main' panicked at …/std/src/io/stdio.rs:1165:9:
failed printing to stdout: No space left on device (os error 28)

Instead of the correct GNU behavior:

tail: write error: No space left on device

Cause

HeaderPrinter::print used println! to write the ==> file <== headers to stdout. println! panics on write failure rather than returning a Result, so any write error (such as ENOSPC from /dev/full) caused an unhandled panic instead of a clean error message.

Fix

Replaced println! in HeaderPrinter with write_all and flush on a locked stdout, both of which return io::Result. The methods now return io::Result and propagate write errors upstream instead of panicking.

@github-actions

github-actions Bot commented May 14, 2026 •

Copy link
Copy Markdown

GNU testsuite comparison:

Skip an intermittent issue tests/cut/bounded-memory (fails in this run but passes in the 'main' branch)
Skip an intermittent issue tests/date/date-locale-hour (fails in this run but passes in the 'main' branch)
Skip an intermittent issue tests/date/resolution (fails in this run but passes in the 'main' branch)
Skip an intermittent issue tests/tail/tail-n0f (fails in this run but passes in the 'main' branch)
Congrats! The gnu test tests/expand/bounded-memory is now passing!
Congrats! The gnu test tests/printf/printf-surprise is now passing!

@gabrielhnf
gabrielhnf force-pushed the fix-tail-header-write-error branch from b9d2a4a to 014cb8d Compare May 14, 2026 19:24
@gabrielhnf

Copy link
Copy Markdown
Contributor Author

Accidentally force-pushed the wrong commit onto the PR branch for a few minutes. I’ve restored the branch to the correct state now, sorry about the noise.

@codspeed

codspeed Bot commented May 15, 2026

Copy link
Copy Markdown

Merging this PR will degrade performance by 19.55%

❌ 1 regressed benchmark
✅ 316 untouched benchmarks
⏩ 46 skipped benchmarks1

Warning

Please fix the performance issues or acknowledge them on CodSpeed.

Performance Changes

Mode Benchmark BASE HEAD Efficiency
❌ Memory cp_recursive_deep_tree[(120, 4)] 562.5 KB 699.2 KB -19.55%

Tip

Investigate this regression by commenting @codspeedbot fix this regression on this PR, or directly use the CodSpeed MCP with your agent.


Comparing gabrielhnf:fix-tail-header-write-error (b9d2a4a) with main (6a942ba)

Open in CodSpeed

Footnotes

  1. 46 benchmarks were skipped, so the baseline results were used instead. If they were deleted from the codebase, click here and archive them to remove them from the performance reports. ↩

Comment thread src/uu/tail/src/paths.rs Outdated
if self.verbose {
println!(
"{}==> {string} <==",
let header_string = format!(

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why format! + write_all + flush? writeln!(stdout().lock(), ...)? should be enough, and it would avoid the allocation (codspeed shows a memory regression)

Comment thread src/uu/tail/src/paths.rs Outdated
let mut stdout = stdout.lock();

stdout.write_all(header_string.as_bytes())?;
stdout.flush()?;

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

do we need this flush? stdout is line buffered anyway

Comment thread tests/by-util/test_tail.rs Outdated
#[cfg(target_os = "linux")]
#[test]
fn test_multiple_input_files_failed_write_is_reported() {
new_ucmd!()

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

please don't depend on /etc/hosts, create two files with at.write() instead

@gabrielhnf
gabrielhnf force-pushed the fix-tail-header-write-error branch from b9d2a4a to 34bb076 Compare October 20, 2026 14:09
@gabrielhnf

Copy link
Copy Markdown
Contributor Author

Hi @sylvestre! I've updated the PR and addressed your review comments:

  • Replaced the format! + write_all approach with writeln!(stdout().lock(), ...)?.
  • Removed the unnecessary explicit flush().
  • Updated the regression test to create its own files using at.write() instead of relying on /etc/hosts.

The changes are pushed, and CI is green.

Thanks for the review!

@sylvestre
sylvestre merged commit 6bd1f8d into uutils:main Oct 22, 2026
138 of 139 checks passed
@sylvestre

Copy link
Copy Markdown
Contributor

Thanks for your PR

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

tail file2 file2 >/dev/full panics

2 participants