{"record":{"id":"3f8d2ddc02aef18a","repo":"headroomlabs-ai/headroom","slug":"hnsw-m-must-be-positive-got-self-hnsw-m","errorCode":null,"errorMessage":"hnsw_m must be positive, got {self.hnsw_m}","messagePattern":"hnsw_m must be positive, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"headroom/memory/config.py","lineNumber":145,"sourceCode":"    cache_enabled: bool = True\n    cache_max_size: int = 1000\n\n    # Bubbling defaults\n    auto_bubble: bool = True\n    bubble_threshold: float = 0.7  # Minimum importance for bubbling\n\n    def __post_init__(self) -> None:\n        \"\"\"Validate configuration after initialization.\"\"\"\n        if self.vector_dimension < 1:\n            raise ValueError(f\"vector_dimension must be positive, got {self.vector_dimension}\")\n\n        if self.hnsw_ef_construction < 1:\n            raise ValueError(\n                f\"hnsw_ef_construction must be positive, got {self.hnsw_ef_construction}\"\n            )\n\n        if self.hnsw_m < 1:\n            raise ValueError(f\"hnsw_m must be positive, got {self.hnsw_m}\")\n\n        if self.hnsw_ef_search < 1:\n            raise ValueError(f\"hnsw_ef_search must be positive, got {self.hnsw_ef_search}\")\n\n        if self.cache_max_size < 1:\n            raise ValueError(f\"cache_max_size must be positive, got {self.cache_max_size}\")\n\n        if self.embedder_backend == EmbedderBackend.OPENAI and not self.openai_api_key:\n            raise ValueError(\"openai_api_key is required when using OpenAI embedder backend\")\n\n        # Ensure db_path is a Path object\n        if isinstance(self.db_path, str):\n            self.db_path = Path(self.db_path)\n","sourceCodeStart":127,"sourceCodeEnd":159,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/headroom/memory/config.py#L127-L159","documentation":"ValueError raised in MemoryConfig.__post_init__ when hnsw_m < 1. hnsw_m is the HNSW graph fan-out (max connections per node); it must be at least 1 for the index to be constructible, and the guard rejects invalid values at config time rather than deep inside index building.","triggerScenarios":"MemoryConfig(hnsw_m=0) or negative, typically from a template placeholder, an unset env var coerced to 0, or hand-tuned YAML with a typo.","commonSituations":"Config files with commented-out defaults replaced by 0; automation writing '0' for missing numeric fields; tuning copied from a different ANN library's scale.","solutions":["Use a positive value, typically 8-48 (16 is a common default); or omit the field for defaults","Sanitize generated configs: treat 0/empty numeric fields as 'unset' and drop them before construction","Add a config lint step in CI for memory settings"],"exampleFix":"# before\ncfg = MemoryConfig(hnsw_m=0)  # ValueError: hnsw_m must be positive\n\n# after\ncfg = MemoryConfig(hnsw_m=16)","handlingStrategy":"validation","validationCode":"def positive_int(name: str, v: int) -> int:\n    if v < 1:\n        raise ValueError(f'{name} must be >= 1, got {v}')\n    return v\n\ncfg = MemoryConfig(hnsw_m=positive_int('hnsw_m', raw_m))","typeGuard":"def is_positive_int(v) -> bool:\n    return isinstance(v, int) and not isinstance(v, bool) and v >= 1","tryCatchPattern":"try:\n    cfg = MemoryConfig(hnsw_m=raw_m)\nexcept ValueError as e:\n    raise SystemExit(f'Bad memory config: {e}') from e","preventionTips":["Drop zero-valued optional fields from parsed config before construction","Use realistic HNSW M values (8-48)","Lint memory config files in CI"],"tags":["python","config","validation","hnsw","vector-index","range-check"],"backgroundTag":null,"analyzedSha":"322425c43bffde1ed0b64fecf3cf5951565dd82b","analyzedAt":"2026-08-15T01:03:05.481Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}