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.

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)

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.