{"record":{"id":"364a3c8f22a4b57b","repo":"dgraph-io/badger","slug":"invalid-checksum-length-in-footer-data-corrupted","errorCode":null,"errorMessage":"invalid checksum length in footer. Data corrupted","messagePattern":"invalid checksum length in footer\\. Data corrupted","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"table/table.go","lineNumber":442,"sourceCode":"\n// initIndex reads the index and populate the necessary table fields and returns\n// first block offset\nfunc (t *Table) initIndex() (*fb.BlockOffset, error) {\n\treadPos := t.tableSize\n\n\t// Read checksum len from the last 4 bytes.\n\tif readPos < 4 {\n\t\treturn nil, errors.New(\"invalid table size in footer. Data corrupted\")\n\t}\n\treadPos -= 4\n\tbuf := t.readNoFail(readPos, 4)\n\tchecksumLen := int(y.BytesToU32(buf))\n\t// checksumLen == 0 is legal (a zero checksum marshals to nothing), so only\n\t// reject negative lengths and lengths that don't fit in the bytes remaining\n\t// before readPos. The < 0 guard catches a uint32 value >= 2^31 wrapping to a\n\t// negative int on 32-bit platforms.\n\tif checksumLen < 0 || checksumLen > readPos {\n\t\treturn nil, errors.New(\"invalid checksum length in footer. Data corrupted\")\n\t}\n\n\t// Read checksum.\n\texpectedChk := &pb.Checksum{}\n\treadPos -= checksumLen\n\tbuf = t.readNoFail(readPos, checksumLen)\n\tif err := proto.Unmarshal(buf, expectedChk); err != nil {\n\t\treturn nil, err\n\t}\n\n\t// Read index size from the footer.\n\tif readPos < 4 {\n\t\treturn nil, errors.New(\"invalid table size in footer. Data corrupted\")\n\t}\n\treadPos -= 4\n\tbuf = t.readNoFail(readPos, 4)\n\tt.indexLen = int(y.BytesToU32(buf))\n\t// A table always has at least one block, so a zero indexLen is always","sourceCodeStart":424,"sourceCodeEnd":460,"githubUrl":"https://github.com/dgraph-io/badger/blob/2a001d466f6b71a917319a1db41f99860e16e269/table/table.go#L424-L460","documentation":"After reading checksumLen from the footer's last 4 bytes, initIndex validates it: a negative value (uint32 >= 2^31 wrapped on 32-bit platforms) or a value larger than the bytes remaining before the footer position means the footer is garbage, so the table is rejected as corrupted.","triggerScenarios":"Opening a table whose last 4 bytes decode to an absurd checksum length — the file was written by a different/incompatible format version, was corrupted in transit, or its tail was overwritten.","commonSituations":"Mixing SST files across incompatible Badger versions; rsync/ftp in ASCII mode or with corruption; tail-sector disk damage; editing or truncating SST files manually.","solutions":["Verify the file wasn't produced by a different Badger/LSM format version","Re-transfer the file and compare checksums (md5sum) with the source","Quarantine the corrupt table file and restore from backup","Rebuild the DB via db.Load(db.Backup output) onto a fresh directory"],"exampleFix":"// before: mixing sst files from badger v2 dir into v3 data dir\n\n// after: migrate via export/import\ndb.Backup(w, 0)        // from old version DB\nnewdb.Load(r, 0)       // into fresh v3 directory","handlingStrategy":"validation","validationCode":"if len(data) < 4 {\n    return errors.New(\"file too small to contain a footer\")\n}\nchkLen := binary.LittleEndian.Uint32(data[len(data)-4:])\nif int(chkLen) > len(data)-4 {\n    return errors.New(\"footer checksum length implausible — corrupt or wrong version\")\n}","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Use the same Badger version for writing and reading a data directory","Verify md5/sha of files after transfer between machines","Avoid manual edits or truncation of SST files"],"tags":["data-corruption","checksum","footer","storage"],"backgroundTag":"sst-footer-corruption","analyzedSha":"2a001d466f6b71a917319a1db41f99860e16e269","analyzedAt":"2026-09-05T13:00:02.264Z","contentChangedAt":"2026-09-05T13:00:02.264Z","schemaVersion":2},"datasetVersion":"2026-09-12T17:17:11.597Z"}