uutils/coreutils · error · io::Error

od-error-skip-past-end

Error message

od-error-skip-past-end

What it means

od's MultifileReader::skip advances past n_skip bytes across the sequence of input files, advancing to the next file whenever the current one is exhausted. If, after consuming all input files, bytes remain to skip (n_skip > 0), the input is shorter than the requested skip amount and it returns an UnexpectedEof io::Error carrying this localized message. od throws it because byte offsets past the end of all inputs are undefined for output positioning.

Source

Thrown at src/uu/od/src/multifile_reader.rs:156

    /// special file (e.g. `/dev/null`, which can be skipped past its empty end).
    /// Everything else - proc/sys files that report a bogus size, pipes, stdin -
    /// is advanced by reading and discarding. Skipping past the end of the whole
    /// input is an error, matching GNU `od`.
    pub fn skip(&mut self, mut n_skip: u64) -> io::Result<()> {
        while n_skip > 0 {
            let Some(curr) = self.curr_file.as_mut() else {
                break;
            };
            n_skip = skip_in_file(curr, n_skip)?;
            if n_skip == 0 {
                break;
            }
            // Current file is exhausted; continue skipping in the next one.
            self.next_file();
        }

        if n_skip > 0 {
            return Err(io::Error::new(
                io::ErrorKind::UnexpectedEof,
                translate!("od-error-skip-past-end"),
            ));
        }
        Ok(())
    }
}

/// Skip up to `n_skip` bytes within a single file. Returns the number of bytes
/// that still need to be skipped (0 if the skip landed inside this file, or
/// the remainder if the file ended first).
fn skip_in_file(curr: &mut CurrentReader, n_skip: u64) -> io::Result<u64> {
    #[cfg(unix)]
    if let CurrentReader::File(f) = curr
        && let Ok(meta) = f.metadata()
    {
        let size = meta.len();
        let blksize = uucore::fs::sane_blksize::sane_blksize_from_metadata(&meta);

View on GitHub (pinned to 325183372a)

Solutions

  1. Reduce the --skip-bytes value to at most the total input size (`stat -c %s file1 file2 | paste -sd+ | bc`).
  2. Verify the input files are complete (re-download/re-copy truncated files) and check their sizes.
  3. Check the skip unit — GNU od accepts suffixes like b (blocks of 512); a wrong unit can overshoot the file.
  4. If skipping in a pipeline, ensure upstream producers actually wrote enough bytes before od reads.

Example fix

// before
od --skip-bytes=100000 small.txt   // file is only 4 KB
// after
size=$(stat -c %s small.txt); od --skip-bytes=$((size > 100000 ? 100000 : size - 1)) small.txt
Defensive patterns

Strategy: validation

Validate before calling

let total: u64 = std::fs::metadata("input.txt")?.len();
let skip: u64 = 100000;
if skip > total {
    eprintln!("skip {skip} exceeds input size {total}");
    // clamp or load a larger input
}

Type guard

fn skip_fits(skip: u64, inputs: &[&str]) -> std::io::Result<bool> {
    let total: u64 = inputs.iter().try_fold(0u64, |acc, f| Ok(acc + std::fs::metadata(f)?.len()))?;
    Ok(skip <= total)
}

Try / catch

match od_result {
    Err(e) if e.kind() == std::io::ErrorKind::UnexpectedEof => {
        // skip past end: clamp skip to total input size and retry
    }
    r => r?,
}

Prevention

When it happens

Trigger: Running `od` with `--skip-bytes=N` (or equivalent skip option) where N exceeds the total size of all input files combined; also via open_input_peek_reader which calls skip during reader setup.

Common situations: Script computing skip offsets from a larger file but reading a truncated/rotated one; off-by-one or wrong unit (chars vs bytes) in skip arguments; piping/copying partial logs; combining multiple small files while assuming a larger total.


AI-assisted analysis of uutils/coreutils@325183372a (2026-08-31). Data as JSON: /api/errors/560dc1343a398410. Report an issue: GitHub.