{"record":{"id":"4e70161c17ce0655","repo":"bytedance/deer-flow","slug":"confidence","errorCode":null,"errorMessage":"confidence","messagePattern":"confidence","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"backend/packages/harness/deerflow/agents/memory/backends/deermem/deermem/core/updater.py","lineNumber":54,"sourceCode":"# context.  Unlike the previous asyncio.run() approach, this runs *sync*\n# model.invoke() calls — no event loop is created, so the langchain async\n# httpx client pool (globally cached via @lru_cache) is never touched and\n# cross-loop connection reuse is impossible.\n_SYNC_MEMORY_UPDATER_EXECUTOR = concurrent.futures.ThreadPoolExecutor(\n    max_workers=4,\n    thread_name_prefix=\"memory-updater-sync\",\n)\natexit.register(lambda: _SYNC_MEMORY_UPDATER_EXECUTOR.shutdown(wait=False))\n\n\n# Data-access + fact-CRUD functions (_save_memory_to_file / get_memory_data /\n# reload_memory_data / import_memory_data / clear_memory_data / create_memory_fact /\n# delete_memory_fact / update_memory_fact) moved into MemoryUpdater as instance\n# methods (use self._storage). See the class below.\ndef _validate_confidence(confidence: float) -> float:\n    \"\"\"Validate persisted fact confidence so stored JSON stays standards-compliant.\"\"\"\n    if not math.isfinite(confidence) or confidence < 0 or confidence > 1:\n        raise ValueError(\"confidence\")\n    return confidence\n\n\ndef _coerce_source_confidence(fact: dict[str, Any]) -> float:\n    \"\"\"Return a stored fact's confidence as a finite float in [0, 1], defaulting to 0.5.\n\n    dict.get(key, default) returns the stored value (including None) when the key\n    exists, so a fact written with \"confidence\": null would propagate None into\n    arithmetic and crash max(). This helper guards against null, bool, non-numeric,\n    and non-finite values from corrupted or manually edited memory files.\n    \"\"\"\n    raw = fact.get(\"confidence\")\n    if raw is None or isinstance(raw, bool):\n        return 0.5\n    try:\n        val = float(raw)\n    except (TypeError, ValueError):\n        return 0.5","sourceCodeStart":36,"sourceCodeEnd":72,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/packages/harness/deerflow/agents/memory/backends/deermem/deermem/core/updater.py#L36-L72","documentation":"_validate_confidence() enforces that a fact's confidence is a finite number in [0, 1] before it is persisted, so stored JSON stays standards-compliant (no NaN/Infinity) and downstream max()/comparison arithmetic is safe. math.isfinite(NaN) is False and out-of-range values are rejected. The terse 'confidence' message is the deliberate contract.","triggerScenarios":"create/update fact paths passing confidence=float('nan'), float('inf'), -0.5, 1.2, or a non-float that reaches math.isfinite (e.g. a string if type coercion was skipped upstream).","commonSituations":"LLM emitting confidence as a percentage (0-100) instead of a fraction; parsing confidence from user input or JSON strings like '0.8'; NaN leaking from arithmetic on missing values (0/0).","solutions":["Clamp and coerce before saving: confidence = min(1.0, max(0.0, float(confidence))).","If the source uses percentages, divide by 100 at the boundary.","Guard NaN explicitly: if not math.isfinite(x): use the 0.5 default.","Validate the whole fact with a pydantic model using Field(ge=0, le=1, allow_inf_nan=False)."],"exampleFix":"# before\nfact = {\"content\": text, \"confidence\": llm_score}  # llm_score in 0..100 or NaN\n\n# after\nscore = float(llm_score) if llm_score is not None and math.isfinite(float(llm_score)) else 0.5\nfact = {\"content\": text, \"confidence\": min(1.0, max(0.0, score / 100.0 if score > 1 else score))}","handlingStrategy":"validation","validationCode":"def safe_confidence(raw) -> float:\n    try:\n        value = float(raw)\n    except (TypeError, ValueError):\n        return 0.5\n    if not math.isfinite(value):\n        return 0.5\n    if value > 1 and value <= 100:  # percentage heuristic\n        value /= 100.0\n    return min(1.0, max(0.0, value))\n\nfact[\"confidence\"] = safe_confidence(fact.get(\"confidence\"))","typeGuard":"def is_valid_confidence(v: object) -> TypeGuard[float]:\n    return isinstance(v, (int, float)) and not isinstance(v, bool) and math.isfinite(v) and 0 <= v <= 1","tryCatchPattern":null,"preventionTips":["Normalize LLM confidence scores (often 0-100 or 0-10) to [0,1] at extraction time.","Use pydantic Field(ge=0, le=1, allow_inf_nan=False) on fact models.","Default missing/invalid confidence to 0.5 instead of propagating NaN."],"tags":["memory","validation","numeric","deermem"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}