dgraph-io/badger · error

Block size cannot be zero

Error message

Block size cannot be zero

What it means

OpenTable requires a nonzero BlockSize whenever the table is compressed. BlockSize is used to size the decompression buffer for blocks, so a zero BlockSize with compression other than options.None makes block decoding impossible; OpenTable closes the mmap file and returns this error.

Solutions

  1. Set opts.BlockSize to the same block size used when the table was built (default badger block size, e.g. 4KB) whenever opts.Compression != options.None
  2. Pass the table's original options rather than a zero-value Options{}; or set Compression to options.None if the table is truly uncompressed
  3. Use the DB-level open path (badger.Open) instead of calling OpenTable directly so correct options are loaded from the manifest
  4. If writing a test, mirror the writer's options: BlockSize = builder's block size

Example fix

// before
tbl, err := table.OpenTable(mf, table.Options{Compression: options.ZSTD}) // BlockSize 0
// after
tbl, err := table.OpenTable(mf, table.Options{Compression: options.ZSTD, BlockSize: 4096})
Defensive patterns

Strategy: validation

Validate before calling

if opts.Compression != options.None && opts.BlockSize == 0 {
	return errors.New("BlockSize must be nonzero when compression is enabled")
}

Type guard

func openTableOptsValid(o table.Options) bool {
	return o.Compression == options.None || o.BlockSize > 0
}

Try / catch

tbl, err := table.OpenTable(mf, opts)
if err != nil {
	if err.Error() == "Block size cannot be zero" {
		opts.BlockSize = 4096 // badger default
		tbl, err = table.OpenTable(mf, opts)
	}
	if err != nil { return nil, err }
}

Prevention

When it happens

Trigger: Calling OpenTable with opts.Compression set to Snappy/ZSTD while opts.BlockSize is 0 (zero-value Options); reading an existing table with wrong OpenTable options where BlockSize wasn't propagated from the DB's stored options.

Common situations: Constructing table.Options by hand for a one-off table read and forgetting BlockSize; copying Options across Badger versions where the field was renamed or defaulted differently; tests opening tables with empty Options{} while the table was written with compression enabled.

Related errors


AI-assisted analysis of dgraph-io/badger@2a001d466f (2026-09-05). Data as JSON: /api/errors/dcfa3ff20fa36d5b. Report an issue: GitHub.

Appendix: source

Thrown at table/table.go:273

	written := bd.Copy(mf.Data)
	y.AssertTrue(written == len(mf.Data))
	if err := z.Msync(mf.Data); err != nil {
		return nil, y.Wrapf(err, "while calling msync on %s", fname)
	}
	return OpenTable(mf, *builder.opts)
}

// OpenTable assumes file has only one table and opens it. Takes ownership of fd upon function
// entry. Returns a table with one reference count on it (decrementing which may delete the file!
// -- consider t.Close() instead). The fd has to writeable because we call Truncate on it before
// deleting. Checksum for all blocks of table is verified based on value of chkMode.
func OpenTable(mf *z.MmapFile, opts Options) (*Table, error) {
	// BlockSize is used to compute the approximate size of the decompressed
	// block. It should not be zero if the table is compressed.
	if opts.BlockSize == 0 && opts.Compression != options.None {
		_ = mf.Close(-1)
		return nil, errors.New("Block size cannot be zero")
	}
	fileInfo, err := mf.Fd.Stat()
	if err != nil {
		mf.Close(-1)
		return nil, y.Wrap(err, "")
	}

	filename := fileInfo.Name()
	id, ok := ParseFileID(filename)
	if !ok {
		mf.Close(-1)
		return nil, fmt.Errorf("Invalid filename: %s", filename)
	}
	t := &Table{
		MmapFile:   mf,
		id:         id,
		opt:        &opts,
		IsInmemory: false,

View on GitHub (pinned to 2a001d466f)