{"record":{"id":"623f5084c0c3e250","repo":"FiloSottile/age","slug":"failed-to-read-chunk-at-offset-d-w","errorCode":null,"errorMessage":"failed to read chunk at offset %d: %w","messagePattern":"failed to read chunk at offset (.+?): %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/stream/stream.go","lineNumber":434,"sourceCode":"\tif len(p) == 0 {\n\t\treturn 0, nil\n\t}\n\tvar cacheUpdate *cachedChunk\n\tchunk := make([]byte, encChunkSize)\n\tfor len(p) > 0 && off < r.size {\n\t\tchunkIndex := off / ChunkSize\n\t\tchunkOff := chunkIndex * encChunkSize\n\t\tencSize := r.size + r.chunks*chacha20poly1305.Overhead\n\t\tchunkSize := min(encSize-chunkOff, encChunkSize)\n\n\t\tcached := r.cache.Load()\n\t\tvar plaintext []byte\n\t\tif cached != nil && cached.off == chunkOff {\n\t\t\tplaintext = cached.data\n\t\t\tcacheUpdate = nil\n\t\t} else {\n\t\t\tif err := readFullAt(r.src, chunk[:chunkSize], chunkOff); err != nil {\n\t\t\t\treturn n, fmt.Errorf(\"failed to read chunk at offset %d: %w\", chunkOff, err)\n\t\t\t}\n\t\t\tnonce := nonceForChunk(chunkIndex)\n\t\t\tif chunkIndex == r.chunks-1 {\n\t\t\t\tsetLastChunkFlag(nonce)\n\t\t\t}\n\t\t\tvar err error\n\t\t\tplaintext, err = r.a.Open(chunk[:0], nonce[:], chunk[:chunkSize], nil)\n\t\t\tif err != nil {\n\t\t\t\treturn n, fmt.Errorf(\"failed to decrypt and authenticate chunk at offset %d: %w\", chunkOff, err)\n\t\t\t}\n\t\t\tcacheUpdate = &cachedChunk{off: chunkOff, data: plaintext}\n\t\t}\n\n\t\tplainChunkOff := int(off - chunkIndex*ChunkSize)\n\t\tcopySize := min(len(plaintext)-plainChunkOff, len(p))\n\t\tcopy(p, plaintext[plainChunkOff:plainChunkOff+copySize])\n\t\tp = p[copySize:]\n\t\toff += int64(copySize)","sourceCodeStart":416,"sourceCodeEnd":452,"githubUrl":"https://github.com/FiloSottile/age/blob/b74dce4cdbe35b5e5f66c06d9612b72f89028758/internal/stream/stream.go#L416-L452","documentation":"DecryptReaderAt.ReadAt failed to read the ciphertext bytes for the chunk covering the requested offset from the underlying io.ReaderAt. readFullAt converted a short read into io.ErrUnexpectedEOF, or the source returned a hard error. The wrapped error identifies the true cause; decryption never ran because the ciphertext was not available.","triggerScenarios":"ReadAt(p, off) where the chunk at chunkIndex*65552 cannot be fully read: src shorter than the declared size (truncated file); a flaky ReaderAt (network file, remote block store) erroring mid-read; an incorrect custom ReaderAt implementation.","commonSituations":"Remote/network storage that shrank or lost data between NewDecryptReaderAt and ReadAt; sparse or partially downloaded files; disk errors; a ReaderAt wrapper that mis-reports n and err per the io.ReaderAt contract.","solutions":["Verify the source still contains all ciphertext bytes; compare current length with the size used at NewDecryptReaderAt.","Re-fetch/re-sync the underlying data if it was truncated or partially downloaded.","If using a custom ReaderAt, fix it to return io.EOF on end-of-data and (n, err) exactly per the interface contract.","Retry if the underlying error is transient (the %w wrapper tells you which it was)."],"exampleFix":null,"handlingStrategy":"try-catch","validationCode":null,"typeGuard":null,"tryCatchPattern":"n, err := dr.ReadAt(p, off)\nif err != nil && !errors.Is(err, io.EOF) {\n    var cause error\n    if errors.As(err, &cause) && errors.Is(cause, io.ErrUnexpectedEOF) {\n        return fmt.Errorf(\"ciphertext truncated or source shrank: %w\", err)\n    }\n    // otherwise retry if transient\n    return err\n}","preventionTips":["Keep the source stable and fully downloaded before random-access decryption.","Re-validate the size against the live source if the storage can change underneath you.","Implement custom ReaderAts strictly per the io.ReaderAt contract."],"tags":["stream","io","readerat","truncated-data","go"],"backgroundTag":"truncated-ciphertext","analyzedSha":"b74dce4cdbe35b5e5f66c06d9612b72f89028758","analyzedAt":"2026-08-31T23:59:31.627Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T05:18:18.240Z"}