#12958·weaviate

flat.Delete leaves AlreadyIndexed() live-vector count stale

Author: leemeiiCreated Sep 7, 2026Updated Sep 10, 2026
Labelsbugcommunity

How to reproduce this bug?

Use the following minimal reproduction script. It temporarily creates a Go test file, runs the test, and removes the file afterward.

python
#!/usr/bin/env python3
import os
import subprocess
import sys
from pathlib import Path

root = Path(os.environ.get("WEAVIATE_ROOT", ".")).resolve()
test_file = root / "adapters/repos/db/vector/flat/audit_delete_count_test.go"

go_test = r'''
package flat

import (
    "context"
    "fmt"
    "testing"

    "github.com/sirupsen/logrus/hooks/test"
    "github.com/stretchr/testify/require"
    "github.com/weaviate/weaviate/adapters/repos/db/lsmkv"
    "github.com/weaviate/weaviate/adapters/repos/db/vector/hnsw/distancer"
    flatent "github.com/weaviate/weaviate/entities/vectorindex/flat"
)

func TestFlatDeleteCount(t *testing.T) {
    logger, _ := test.NewNullLogger()
    root := t.TempDir()
    store := testStore(t, root, logger)
    defer store.Shutdown(context.Background())

    cfg := flatent.UserConfig{}
    cfg.SetDefaults()

    idx, err := New(Config{
        ID: "audit",
        RootPath: root,
        DistanceProvider: distancer.NewCosineDistanceProvider(),
        MakeBucketOptions: lsmkv.MakeRegularBucketOptions,
    }, cfg, store)
    require.NoError(t, err)
    defer idx.Shutdown(context.Background())

    require.NoError(t, idx.Add(context.Background(), 7, []float32{1, 0}))
    require.NoError(t, idx.Add(context.Background(), 8, []float32{0, 1}))

    before := idx.AlreadyIndexed()
    require.NoError(t, idx.Delete(7))
    after := idx.AlreadyIndexed()

    fmt.Printf(
        "before=%d after=%d contains_deleted=%v remaining=%v\n",
        before, after, idx.ContainsDoc(7), idx.ContainsDoc(8),
    )

    if before != 2 || after != 1 ||
        idx.ContainsDoc(7) || !idx.ContainsDoc(8) {
        t.Fatalf("unexpected live-vector state")
    }
}
'''

if not (root / "go.mod").exists():
    print("WEAVIATE_ROOT must point to a Weaviate checkout", file=sys.stderr)
    sys.exit(2)

test_file.write_text(go_test)

try:
    result = subprocess.run(
        [
            os.environ.get("GO", "go"),
            "test",
            "./adapters/repos/db/vector/flat",
            "-run",
            "^TestFlatDeleteCount$",
            "-count=1",
            "-v",
        ],
        cwd=root,
        text=True,
        capture_output=True,
    )
finally:
    test_file.unlink(missing_ok=True)

print(result.stdout, end="")
print(result.stderr, end="", file=sys.stderr)

if (
    result.returncode == 1
    and "before=2 after=2 contains_deleted=false remaining=true" in result.stdout
):
    print("BUG REPRODUCED")
    sys.exit(0)

if result.returncode == 0 and "before=2 after=1" in result.stdout:
    print("BUG NOT REPRODUCED")
    sys.exit(1)

print("ENVIRONMENT OR ORACLE FAILURE", file=sys.stderr)
sys.exit(2)

Run:

bash
WEAVIATE_ROOT=/path/to/weaviate python3 repro.py

What is the expected behavior?

After:

Add(7)
Add(8)
Delete(7)

only vector ID 8 remains. The expected output is:

before=2 after=1 contains_deleted=false remaining=true

Therefore:

AlreadyIndexed() == 1
ContainsDoc(7) == false
ContainsDoc(8) == true

What is the actual behavior?

The actual output is:

before=2 after=2 contains_deleted=false remaining=true

The deletion correctly removes ID 7, but AlreadyIndexed() continues to report 2.

Supporting information

No response

Server Version

1.40.0

Weaviate Setup

Single Node

Nodes count

No response

Code of Conduct