{"record":{"id":"52883a04ee974104","repo":"hyperledger/fabric","slug":"unexpected-end-of-blockfile","errorCode":null,"errorMessage":"unexpected end of blockfile","messagePattern":"unexpected end of blockfile","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"warning","filePath":"common/ledger/blkstorage/block_stream.go","lineNumber":22,"sourceCode":"SPDX-License-Identifier: Apache-2.0\n*/\n\npackage blkstorage\n\nimport (\n\t\"bufio\"\n\t\"fmt\"\n\t\"io\"\n\t\"os\"\n\n\t\"github.com/pkg/errors\"\n\t\"google.golang.org/protobuf/encoding/protowire\"\n)\n\n// ErrUnexpectedEndOfBlockfile error used to indicate an unexpected end of a file segment\n// this can happen mainly if a crash occurs during appending a block and partial block contents\n// get written towards the end of the file\nvar ErrUnexpectedEndOfBlockfile = errors.New(\"unexpected end of blockfile\")\n\n// blockfileStream reads blocks sequentially from a single file.\n// It starts from the given offset and can traverse till the end of the file\ntype blockfileStream struct {\n\tfileNum       int\n\tfile          *os.File\n\treader        *bufio.Reader\n\tcurrentOffset int64\n}\n\n// blockStream reads blocks sequentially from multiple files.\n// it starts from a given file offset and continues with the next\n// file segment until the end of the last segment (`endFileNum`)\ntype blockStream struct {\n\trootDir           string\n\tcurrentFileNum    int\n\tendFileNum        int\n\tcurrentFileStream *blockfileStream","sourceCodeStart":4,"sourceCodeEnd":40,"githubUrl":"https://github.com/hyperledger/fabric/blob/2736b63f8fd5932511d56fe68b7039d15977f7f6/common/ledger/blkstorage/block_stream.go#L4-L40","documentation":"ErrUnexpectedEndOfBlockfile is a sentinel error in Hyperledger Fabric's block storage indicating that a blockfile segment ends before a complete block could be read. It is thrown when the varint-encoded block length cannot be decoded from the trailing bytes, or when the declared length exceeds the bytes remaining in the file. This mainly happens if a crash occurs while a block was being appended, leaving partial block contents at the end of the file. Callers treat it as an expected condition for torn writes and truncate/recover rather than fail hard.","triggerScenarios":"Calling nextBlockBytesAndPlacementInfo (via nextBlockBytes) when: (1) the last read of the file hits fewer bytes than the varint length prefix needs and no more content is available (block_stream.go:113), or (2) lenBytes + length exceeds remainingBytes in the file (block_stream.go:120). Also deliberately returned by test helper testBlockFileStreamUnexpectedEOF.","commonSituations":"Peer crash or power loss mid-append leaving a partially written block; disk-full during block commit; process killed (SIGKILL) while flush is in flight; scanning a blockfile copied while the peer was still writing to it.","solutions":["Let the ledger recovery path handle it: it is an expected sentinel — allow scanForLastCompleteBlock/recovery to truncate the partial tail at the last complete block boundary","Check disk space and fsync/journal health on the peer's ledger filesystem to prevent torn appends","Do not copy or rsync blockfiles while the peer is running; stop the peer first","If it occurs on a healthy file at offsets other than the end, validate the file with the blkstorage integrity tooling and restore from backup/recompute from a peer snapshot"],"exampleFix":"// before: treating any error from iteration as fatal\nif err := stream.Err(); err != nil {\n    return err // crashes recovery even for torn tail\n}\n// after\nif errors.Is(err, blkstorage.ErrUnexpectedEndOfBlockfile) {\n    logger.Warningf(\"partial block at file end, truncating\")\n    return truncateAfterLastCompleteBlock(file)\n}","handlingStrategy":"type-guard","validationCode":"info, err := os.Stat(blockfilePath)\nif err != nil || info.Size() == 0 {\n    // nothing to read or unreadable file; skip scan of this segment\n}","typeGuard":"func isUnexpectedEOF(err error) bool {\n    return errors.Is(err, blkstorage.ErrUnexpectedEndOfBlockfile)\n}","tryCatchPattern":"if err := iter.Next(); err != nil {\n    if isUnexpectedEOF(err) {\n        logger.Warningf(\"partial block at tail; recovery will truncate\")\n        return recoverFromTornWrite()\n    }\n    return err\n}","preventionTips":["Always shut the peer down cleanly (SIGTERM, not SIGKILL) before maintenance","Keep headroom on the ledger volume so appends never fail mid-block","Never copy or back up blockfiles while the peer is running","Run on storage with working fsync semantics"],"tags":["fabric","ledger","blockfile","torn-write"],"backgroundTag":"unexpected-eof-truncated-file","analyzedSha":"2736b63f8fd5932511d56fe68b7039d15977f7f6","analyzedAt":"2026-09-04T08:52:36.465Z","contentChangedAt":"2026-09-04T08:52:36.465Z","schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}