{"record":{"id":"af44a17a452c4eee","repo":"weaviate/weaviate","slug":"read-key","errorCode":null,"errorMessage":"read key","messagePattern":"read key","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"adapters/repos/db/lsmkv/segment_serialization.go","lineNumber":153,"sourceCode":"\tout.tombstone = tmpBuf[0] != 0\n\tvalueLength := binary.LittleEndian.Uint64(tmpBuf[1:9])\n\tout.value = make([]byte, valueLength)\n\tif n, err := io.ReadFull(r, out.value); err != nil {\n\t\treturn out, errors.Wrap(err, \"read value\")\n\t} else {\n\t\tout.offset += n\n\t}\n\n\tif n, err := io.ReadFull(r, tmpBuf[0:4]); err != nil {\n\t\treturn out, errors.Wrap(err, \"read key length encoding\")\n\t} else {\n\t\tout.offset += n\n\t}\n\n\tkeyLength := binary.LittleEndian.Uint32(tmpBuf[0:4])\n\tout.primaryKey = make([]byte, keyLength)\n\tif n, err := io.ReadFull(r, out.primaryKey); err != nil {\n\t\treturn out, errors.Wrap(err, \"read key\")\n\t} else {\n\t\tout.offset += n\n\t}\n\n\tout.secondaryIndexCount = secondaryIndexCount\n\tif secondaryIndexCount > 0 {\n\t\tout.secondaryKeys = make([][]byte, secondaryIndexCount)\n\t}\n\n\tfor j := 0; j < int(secondaryIndexCount); j++ {\n\t\tif n, err := io.ReadFull(r, tmpBuf[0:4]); err != nil {\n\t\t\treturn out, errors.Wrap(err, \"read secondary key length encoding\")\n\t\t} else {\n\t\t\tout.offset += n\n\t\t}\n\t\tsecKeyLen := binary.LittleEndian.Uint32(tmpBuf[0:4])\n\t\tif secKeyLen == 0 {\n\t\t\tcontinue","sourceCodeStart":135,"sourceCodeEnd":171,"githubUrl":"https://github.com/weaviate/weaviate/blob/75aa4b6d11f8818305aafd4440b4e32794f7ca04/adapters/repos/db/lsmkv/segment_serialization.go#L135-L171","documentation":"ParseReplaceNode reads the primary key after decoding its 4-byte length: it allocates keyLength bytes and calls io.ReadFull to fill them. This error means the underlying read returned fewer bytes than keyLength — almost always io.ErrUnexpectedEOF — so the key bytes are missing. Since the key length itself was decoded successfully, this usually indicates a value/length mismatch: a corrupted, truncated, or misparsed record whose key region extends past the end of the data.","triggerScenarios":"Calling ParseReplaceNode (via bucket cursors, compaction, or segment recovery) on a segment whose file ends inside the key region: file truncated after the key-length field, or an earlier desynchronized field caused an absurd keyLength (up to 4 GiB) to be allocated and read from a short reader.","commonSituations":"Torn writes from a crash or power loss; disk-full during a flush; incomplete backup restore; a truncated segment file copied out of band; byte-shifted records after corruption upstream in the same record.","solutions":["Identify the affected segment file from the shard logs, stop Weaviate, and let segment recovery/validation drop or repair the truncated segment.","Restore the shard from a verified backup taken while the instance was stopped or via the backup module.","Check disk space, dmesg/filesystem errors; repair the volume before restarting.","If unrecoverable, delete the shard/collection and re-ingest.","Report to weaviate if reproducible on healthy storage — a writer bug may be producing malformed records."],"exampleFix":"// before: trusting a length parsed from possibly-corrupt bytes\nkeyLength := binary.LittleEndian.Uint32(tmpBuf[0:4])\nout.primaryKey = make([]byte, keyLength)\n// after: validate the declared length against the remaining record budget\nremaining := recEnd - uint64(out.offset)\nif uint64(keyLength) > remaining {\n    return out, fmt.Errorf(\"key length %d exceeds remaining record bytes %d\", keyLength, remaining)\n}\nout.primaryKey = make([]byte, keyLength)","handlingStrategy":"validation","validationCode":"// Validate segment integrity before opening it for reads (while stopped):\nsize := fileSize(segPath)\n// each record's declared keyLength must fit within [recordStart, size]\nif recStart + 9 + valueLen + 4 + uint64(keyLength) > size {\n    return fmt.Errorf(\"segment %s record at %d overruns file\", segPath, recStart)\n}","typeGuard":"func isTruncatedSegmentError(err error) bool {\n    return errors.Is(err, io.ErrUnexpectedEOF)\n}","tryCatchPattern":"if err := lsmkv.ParseReplaceNodeInto(bufReader, secIdxCount, node); err != nil {\n    if isTruncatedSegmentError(err) {\n        log.Warnf(\"segment truncated mid-key, quarantining file: %v\", err)\n        return quarantineSegment(segPath)\n    }\n    return err\n}","preventionTips":["Restore backups completely and verify file sizes/checksums before starting Weaviate.","Avoid terminating the process during compaction or flush operations.","Use redundant/healthy storage; watch for dmesg I/O error reports.","Upgrade Weaviate through supported version paths so segment formats stay consistent."],"tags":["lsmkv","segment-corruption","unexpected-eof","storage","golang"],"backgroundTag":"segment-file-truncated","analyzedSha":"75aa4b6d11f8818305aafd4440b4e32794f7ca04","analyzedAt":"2026-09-04T14:58:20.392Z","contentChangedAt":"2026-09-04T14:58:20.392Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}