weaviate/weaviate · error · ErrIllegalArguments
%w: discriminant size %d, expected %d for level %d
Error message
%w: discriminant size %d, expected %d for level %d
What it means
HashTree.Level() expects a level-local discriminant whose Size() exactly equals the number of nodes at that level (nodesAtLevel(level)). Bit i selects node i, so a wrong size means bit indices are ambiguous. The library throws ErrIllegalArguments to prevent misaligned selection masks.
Source
Thrown at usecases/replica/hashtree/hashtree.go:215
func (ht *HashTree) Level(level int, discriminant *Bitset, digests []Digest) (n int, err error) {
ht.mux.Lock()
defer ht.mux.Unlock()
if level < 0 {
return 0, fmt.Errorf("%w: invalid level(%d)", ErrIllegalArguments, level)
}
if level > ht.Height() {
return 0, fmt.Errorf("%w: level(%d) is too high for current height(%d)", ErrIllegalState, level, ht.height)
}
if discriminant == nil {
return 0, fmt.Errorf("%w: nil discriminant provided", ErrIllegalArguments)
}
expectedSize := nodesAtLevel(level)
if discriminant.Size() != expectedSize {
return 0, fmt.Errorf("%w: discriminant size %d, expected %d for level %d",
ErrIllegalArguments, discriminant.Size(), expectedSize, level)
}
// one digest is written per set bit, so SetCount() capacity suffices (see LevelDiff)
if len(digests) < discriminant.SetCount() {
return 0, fmt.Errorf("%w: output buffer has not enough capacity", ErrIllegalArguments)
}
ht.sync()
offset := InnerNodesCount(level)
for i := 0; i < expectedSize; i++ {
if discriminant.IsSet(i) {
// bound writes even if the cached set count understates the bits
if n == len(digests) {
return 0, fmt.Errorf("%w: discriminant set count understates its set bits", ErrIllegalArguments)
}View on GitHub (pinned to 75aa4b6d11)
Solutions
- Build the discriminant with exactly NewBitset(nodesAtLevel(level)) bits for the level you query.
- If you only have a leaf-level discriminant, derive the per-level mask yourself, or use Diff/NewRangeReader which handle level-local masks internally.
- Verify both trees involved have identical Height() before sharing bitsets between them.
Example fix
// before disc := hashtree.NewBitset(ht.LeavesCount(height)) // leaf-level size ht.Level(l, disc, digests) // after disc := hashtree.NewBitset(nodesAtLevel(l)) // level-local size ht.Level(l, disc, digests)
Defensive patterns
Strategy: validation
Validate before calling
if disc != nil && disc.Size() != nodesAtLevel(level) {
return fmt.Errorf("discriminant size %d != %d for level %d", disc.Size(), nodesAtLevel(level), level)
} Type guard
func isLevelLocalDisc(disc *hashtree.Bitset, level int) bool {
return disc != nil && disc.Size() == nodesAtLevel(level)
} Prevention
- Build level-local masks with NewBitset(nodesAtLevel(level)) per queried level
- Never reuse leaf-level Diff output as a Level() discriminant for inner levels
- Share bitsets only between trees of identical Height()
When it happens
Trigger: Passing a leaf-level discriminant (Size()==LeavesCount(height)) into Level for an inner level, or vice versa; reusing a walk bitset from a different tree height; calling Level on trees of different heights with the same Bitset.
Common situations: Using a discriminant produced by Diff (leaf-level) directly with Level(level,...) for level < Height(); caching bitsets across trees whose heights changed after a schema/shard change; confusion between LeavesCount and nodesAtLevel sizes.
Related errors
- %w: discriminant size %d, expected %d (leaf-level)
- %w: level(%d) is too high for current height(%d)
- %w: nil discriminant provided
- %w: output buffer has not enough capacity
- %w: discriminant set count understates its set bits
AI-assisted analysis of weaviate/weaviate@75aa4b6d11 (2026-09-04).
Data as JSON: /api/errors/d8a66319e9bd90d8.
Report an issue: GitHub.