{"record":{"id":"8d5bd3b43c5419cd","repo":"dgraph-io/badger","slug":"empty-value-v","errorCode":null,"errorMessage":"Empty value: %+v","messagePattern":"Empty value: %\\+v","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"value.go","lineNumber":218,"sourceCode":"\t\tif count%100000 == 0 {\n\t\t\tvlog.opt.Debugf(\"Processing entry %d\", count)\n\t\t}\n\n\t\tif isDeletedOrExpired(e.meta, e.ExpiresAt) {\n\t\t\treturn nil\n\t\t}\n\n\t\tvs, err := vlog.db.get(e.Key)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\tif discardEntry(e, vs, vlog.db) {\n\t\t\treturn nil\n\t\t}\n\n\t\t// Value is still present in value log.\n\t\tif len(vs.Value) == 0 {\n\t\t\treturn fmt.Errorf(\"Empty value: %+v\", vs)\n\t\t}\n\t\tvar vp valuePointer\n\t\tvp.Decode(vs.Value)\n\n\t\t// If the entry found from the LSM Tree points to a newer vlog file, don't do anything.\n\t\tif vp.Fid > f.fid {\n\t\t\treturn nil\n\t\t}\n\t\t// If the entry found from the LSM Tree points to an offset greater than the one\n\t\t// read from vlog, don't do anything.\n\t\tif vp.Offset > e.offset {\n\t\t\treturn nil\n\t\t}\n\t\t// If the entry read from LSM Tree and vlog file point to the same vlog file and offset,\n\t\t// insert them back into the DB.\n\t\t// NOTE: It might be possible that the entry read from the LSM Tree points to\n\t\t// an older vlog file. See the comments in the else part.\n\t\tif vp.Fid == f.fid && vp.Offset == e.offset {","sourceCodeStart":200,"sourceCodeEnd":236,"githubUrl":"https://github.com/dgraph-io/badger/blob/2a001d466f6b71a917319a1db41f99860e16e269/value.go#L200-L236","documentation":"This error is thrown during a value-log read (value.go:218) when the LSM tree says the key is still present (not discarded) but the returned ValueStruct carries a zero-length value, which should be impossible for a vlog-backed entry. It means the on-disk metadata pointing into the value log is inconsistent or truncated, so the read cannot return valid data.","triggerScenarios":"Calling db.Get()/Txn.Get() on a key whose entry in the LSM points to the value log while vs.Value is empty; happens when value-log data was truncated/corrupted (e.g. crash without sync, partial file copy, badger.ValueLogMode misuse) or LSM/vlog files got out of sync.","commonSituations":"Restoring a database by copying only some files (LSM without vlog or vice versa), disk corruption or truncation after power loss, running an older badger version against data written by a newer one, manual deletion of .vlog files.","solutions":["Restore the data files from a known-good backup (LSM tree and .vlog files must come from the same snapshot).","Run badger StreamView/iterator to salvage readable keys, or use the badger 'db.ReplayValueLog' style recovery tooling for the affected vlog file.","Check whether value log files were truncated (compare sizes against value pointers); truncate/repair the offending .vlog with the badger repair tool if available.","If data is disposable, delete the affected .vlog files and let badger GC/rebuild, accepting loss of values still only in the vlog.","Verify the same badger library version is used for writing and reading the data directory."],"exampleFix":"// before: copying DB dir partially\n$ cp dbdir/MANIFEST dbdir/KEYREGISTRY dbdir/*.sst /backup/\n// after: copy everything atomically (LSM + value log together)\n$ badger backup --dir dbdir -f backup.bak\n# or stop writes and copy the entire dbdir including *.vlog files","handlingStrategy":"try-catch","validationCode":"// Go: verify the data directory is complete and consistent before opening\nfunc checkVlogIntegrity(dir string) error {\n    matches, err := filepath.Glob(filepath.Join(dir, \"*.vlog\"))\n    if err != nil || len(matches) == 0 { return fmt.Errorf(\"no vlog files in %s\", dir) }\n    for _, m := range matches {\n        fi, err := os.Stat(m)\n        if err != nil || fi.Size() < 20 { return fmt.Errorf(\"vlog file %s missing or truncated\", m) }\n    }\n    return nil\n}","typeGuard":"func isEmptyValue(vs badger.ValueStruct) bool { return len(vs.Value) == 0 } // treat as corruption signal, not empty data","tryCatchPattern":"err := db.View(func(txn *badger.Txn) error {\n    item, err := txn.Get(key)\n    if err != nil { return err }\n    return item.Value(func(v []byte) error {\n        if len(v) == 0 { return fmt.Errorf(\"corrupt empty value for key %q\", key) }\n        return process(v)\n    })\n})\nif err != nil {\n    // fall back to backup/restore path; do not blind-retry\n    restoreFromBackup(dbDir)\n}","preventionTips":["Always snapshot the entire badger directory (MANIFEST, *.sst, *.vlog) atomically; never copy subsets.","Enable checksums/verify data directories after restore before serving traffic.","Use badger.Stream/backup+load for logical backups instead of raw file copies.","Run the same badger version that wrote the data.","Use fsync-backed storage and avoid force-killing the process (SIGKILL on write path)."],"tags":["go","badger","value-log","corruption","data-integrity"],"backgroundTag":"value-log-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"}