headroomlabs-ai/headroom · error · FileNotFoundError

HNSW index not found: {hnsw_path}

Error message

HNSW index not found: {hnsw_path}

What it means

Raised by HNSWVectorIndex.load_index when the expected .hnsw graph file does not exist at the derived path (path.with_suffix('.hnsw')). The loader treats a missing index file as a hard error rather than silently starting empty, so callers must handle first-run scenarios explicitly.

Source

Thrown at headroom/memory/adapters/hnsw.py:852

    def load_index(self, path: str | Path) -> None:
        """Load the index from disk.

        Loads both the HNSW index and all metadata/mappings.

        Args:
            path: Base path for the saved files.

        Raises:
            FileNotFoundError: If the index files don't exist.
            ValueError: If the saved dimension doesn't match.
        """
        path = Path(path)
        hnsw_path = path.with_suffix(".hnsw")
        meta_path = path.with_suffix(".meta")

        if not hnsw_path.exists():
            raise FileNotFoundError(f"HNSW index not found: {hnsw_path}")
        if not meta_path.exists():
            raise FileNotFoundError(f"Metadata file not found: {meta_path}")

        # Load metadata first to get parameters
        with open(meta_path) as f:
            meta_data = json.load(f)

        # Verify dimension matches
        saved_dimension = meta_data["dimension"]
        if saved_dimension != self._dimension:
            raise ValueError(
                f"Saved index dimension {saved_dimension} does not match "
                f"current dimension {self._dimension}"
            )

        with self._lock:
            # Update parameters
            self._max_elements = meta_data["max_elements"]

View on GitHub (pinned to 322425c43b)

Solutions

  1. Guard with Path.exists(): if the .hnsw file is absent, build a fresh index (and optionally save it) instead of loading.
  2. Verify the exact derived path — load_index appends '.hnsw' via with_suffix, so 'myindex' loads 'my.hnsw' if the stem looks like a suffix; prefer a path without dots.
  3. If the file was deleted unintentionally, restore it or re-index from the source of truth.

Example fix

// before
await index.load_index(path)  # crashes on first run

// after
if path.with_suffix('.hnsw').exists():
    await index.load_index(path)
else:
    for m in memories:
        await index.add_memory(m)
    index.save_index(path)
Defensive patterns

Strategy: type-guard

Validate before calling

if not path.with_suffix('.hnsw').exists():
    # first run: build fresh
    index = HNSWVectorIndex(dimension=embedder.dimension)
else:
    await index.load_index(path)

Type guard

from pathlib import Path
def index_files_exist(base: Path) -> bool:
    return base.with_suffix('.hnsw').exists() and base.with_suffix('.meta').exists()

Try / catch

try:
    await index.load_index(path)
except FileNotFoundError:
    for m in memories:
        await index.add_memory(m)
    index.save_index(path)

Prevention

When it happens

Trigger: Calling load_index on a fresh install where nothing was saved yet; passing a base path whose suffix substitution points elsewhere (e.g. 'index.v1' becomes 'index.hnsw'); deleted or moved index files.

Common situations: First run of an app before any save_index; typos in the path; with_suffix('.hnsw') surprising developers who expected the literal filename to be used; clearing caches/data directories.

Related errors


AI-assisted analysis of headroomlabs-ai/headroom@322425c43b (2026-08-15). Data as JSON: /api/errors/871d6b9e5db6662d. Report an issue: GitHub.