FiloSottile/age · error
failed to read rest of file: %w
Error message
failed to read rest of file: %w
What it means
When the caller doesn't provide a file size (fileSize == -1) or the file is armored, Inspect drains the remaining bytes with io.Copy(io.Discard, rest) to compute the payload size. Any read error from the underlying reader is wrapped with this message.
Source
Thrown at internal/inspect/inspect.go:81
switch s.Type {
case "X25519", "ssh-rsa", "ssh-ed25519", "p256tag", "piv-p256":
data.Postquantum = "no"
case "mlkem768x25519", "scrypt", "mlkem768p256tag":
// Keep "yes".
default:
if data.Postquantum != "no" {
data.Postquantum = "unknown"
}
}
}
// If fileSize is not provided, or if it's the size of the armored file
// (which can have LF or CRLF line endings, varying its size), read to
// the end to determine it.
if fileSize == -1 || data.Armor {
n, err := io.Copy(io.Discard, rest)
if err != nil {
return nil, fmt.Errorf("failed to read rest of file: %w", err)
}
fileSize = data.Sizes.Header + n
if !tr.done {
panic("trackReader not done after io.Copy")
}
if tr.count != fileSize && !data.Armor {
panic("trackReader count mismatch")
}
data.Sizes.Armor = tr.count - fileSize
}
data.Sizes.Overhead, err = streamOverhead(fileSize - data.Sizes.Header)
if err != nil {
return nil, fmt.Errorf("failed to compute stream overhead: %w", err)
}
data.Sizes.MinPayload = fileSize - data.Sizes.Header - data.Sizes.Overhead
data.Sizes.MaxPayload = data.Sizes.MinPayload
return data, nil
}View on GitHub (pinned to b74dce4cdb)
Solutions
- Inspect a local, fully written copy of the file instead of a remote/streaming source
- Verify storage health / network stability for the source path
- Retry the inspection; transient read failures won't corrupt anything
- Provide an accurate fileSize to skip the drain path where applicable
Example fix
// before
data, err := inspect.Inspect(remoteReader, -1) // failed to read rest of file
// after
local, err := os.CreateTemp("", "age"); io.Copy(local, remoteReader); local.Seek(0, 0)
st, _ := local.Stat()
data, err := inspect.Inspect(local, st.Size()) Defensive patterns
Strategy: try-catch
Validate before calling
st, err := os.Stat(path)
if err != nil { return err }
if st.Size() <= 0 { return errors.New("empty file") } Type guard
var pe *age.ParseError
if errors.As(err, &pe) { /* Inspect-level wrapped error */ } Try / catch
data, err := inspect.Inspect(r, -1)
if err != nil {
if errors.Is(err, io.ErrUnexpectedEOF) { return fmt.Errorf("stream cut off during inspection: %w", err) }
return err
} Prevention
- Copy remote/streamed files to local storage before inspecting
- Retry inspection on transient I/O failures
- Check mount/disk health if failures repeat on the same source
When it happens
Trigger: inspect.Inspect runs io.Copy over the post-header remainder and the underlying reader (file, pipe, network) returns an I/O error mid-stream.
Common situations: Inspecting files over unstable mounts/network shares; reading from pipes or sockets that close early; dying disks; armored input truncated mid-body.
Related errors
- failed to read line: %w
- failed to read header: %w
- failed to read header: %w
- failed to re-serialize header: %w
- failed to compute stream overhead: %w
AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31).
Data as JSON: /api/errors/9f3de315f68ca792.
Report an issue: GitHub.