{"record":{"id":"5323cb50ccfa6271","repo":"headroomlabs-ai/headroom","slug":"hnsw-ef-search-must-be-positive-got-self-hnsw-ef","errorCode":null,"errorMessage":"hnsw_ef_search must be positive, got {self.hnsw_ef_search}","messagePattern":"hnsw_ef_search must be positive, got (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"headroom/memory/config.py","lineNumber":148,"sourceCode":"    # 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":130,"sourceCodeEnd":159,"githubUrl":"https://github.com/headroomlabs-ai/headroom/blob/322425c43bffde1ed0b64fecf3cf5951565dd82b/headroom/memory/config.py#L130-L159","documentation":"ValueError raised in MemoryConfig.__post_init__ when hnsw_ef_search < 1. ef_search controls the HNSW query-time candidate list size — the recall/speed tradeoff knob — and must be at least 1 for search to visit any nodes. Validation happens at construction, before the index is used.","triggerScenarios":"MemoryConfig(hnsw_ef_search=0) or negative; often an attempt to 'disable' the parameter or a value sourced from an empty env var defaulted to 0.","commonSituations":"Operators setting 0 expecting default behavior; configs generated from templates with unfilled numeric placeholders; latency tuning gone wrong.","solutions":["Set a positive value (typical range 16-256; higher = better recall, slower search) or omit for the default","Map missing/0 config inputs to the default instead of passing them through","Profile with a valid ef_search rather than 0 when tuning latency"],"exampleFix":"# before\ncfg = MemoryConfig(hnsw_ef_search=0)  # ValueError\n\n# after\ncfg = MemoryConfig(hnsw_ef_search=64)","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_ef_search=positive_int('hnsw_ef_search', raw_ef))","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_ef_search=raw_ef)\nexcept ValueError:\n    cfg = MemoryConfig()  # defaults","preventionTips":["Do not use 0 to mean 'default' — omit the field instead","Tune ef_search with measurements (recall@k vs latency), starting from 64","Validate config templates with schema constraints (minimum: 1)"],"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"}