FiloSottile/age · error
non-EOF error reading after end of encrypted file: %w
Error message
non-EOF error reading after end of encrypted file: %w
What it means
After the DecryptReader emits the last marked chunk, it peeks one more byte from the source to confirm the ciphertext ends cleanly. This error means that peek returned a non-EOF, non-nil error, so the reader cannot certify that the encrypted stream terminated correctly. The decrypted data read so far was authenticated, but the underlying source errored while checking for trailing data.
Source
Thrown at internal/stream/stream.go:105
last, err := r.readChunk()
if err != nil {
r.err = err
return 0, err
}
n := copy(p, r.unread)
r.unread = r.unread[n:]
if last {
// Ensure there is an EOF after the last chunk as expected. In other
// words, check for trailing data after a full-length final chunk.
// Hopefully, the underlying reader supports returning EOF even if it
// had previously returned an EOF to ReadFull.
if _, err := io.ReadFull(r.src, make([]byte, 1)); err == nil {
r.err = errors.New("trailing data after end of encrypted file")
} else if err != io.EOF {
r.err = fmt.Errorf("non-EOF error reading after end of encrypted file: %w", err)
} else {
r.err = io.EOF
}
}
return n, nil
}
// readChunk reads the next chunk of ciphertext from r.src and makes it available
// in r.unread. last is true if the chunk was marked as the end of the message.
// readChunk must not be called again after returning a last chunk or an error.
func (r *DecryptReader) readChunk() (last bool, err error) {
if len(r.unread) != 0 {
panic("stream: internal error: readChunk called with dirty buffer")
}
in := r.buf[:]
n, err := io.ReadFull(r.src, in)View on GitHub (pinned to b74dce4cdb)
Solutions
- Inspect the wrapped error (%w) to identify the underlying reader failure and fix that source first.
- If reading from a network or pipe, ensure the sender closes the stream cleanly so Read returns io.EOF.
- Retry the read/transfer if the source is transient (network glitch) rather than the ciphertext being bad.
- If the source is a local file, check disk health and file integrity.
Defensive patterns
Strategy: try-catch
Type guard
// Go: unwrap to check for a clean EOF vs a source error
if err != nil && !errors.Is(err, io.EOF) {
var srcErr error
if errors.As(err, &srcErr) { /* inspect wrapped reader error */ }
} Try / catch
n, err := reader.Read(buf)
if errors.Is(err, io.EOF) {
// clean end
} else if err != nil {
var wrapped error
if errors.As(err, &wrapped) {
log.Printf("source failed while verifying stream end: %v", wrapped)
}
} Prevention
- Read from stable sources (os.File, bytes.Reader) where possible.
- Ensure writers close pipes/connections cleanly so EOF is delivered.
- Treat non-EOF wrapped errors as source I/O problems, not corruption.
When it happens
Trigger: Calling Read on a DecryptReader until the final chunk is consumed; io.ReadFull(src, 1-byte) after the last chunk returns an error other than io.EOF (e.g. an I/O error from a file, network reader, or pipe).
Common situations: Reading an encrypted file from a flaky network connection or failing disk; a wrapped reader (e.g. an HTTP body or gzip reader) that surfaces a mid-read error at stream end; a pipe/socket closed abruptly instead of returning EOF.
Related errors
- failed to read final chunk: %w
- failed to read chunk at offset %d: %w
- invalid encrypted payload size: %d
- failed to decrypt and authenticate final chunk: %w
- offset out of range [0:%d]: %d
AI-assisted analysis of FiloSottile/age@b74dce4cdb (2026-08-31).
Data as JSON: /api/errors/fa0ba2cc11aa69e0.
Report an issue: GitHub.