{"record":{"id":"1890edb4fc5114b8","repo":"OpenBMB/ChatDev","slug":"cache-ttl-must-be-numeric","errorCode":null,"errorMessage":"cache_ttl must be numeric","messagePattern":"cache_ttl must be numeric","errorType":"validation","errorClass":"ConfigError","httpStatus":null,"severity":"error","filePath":"entity/configs/node/tooling.py","lineNumber":387,"sourceCode":"                raise ConfigError(\"headers must be a mapping\", extend_path(path, \"headers\"))\n            headers = {str(k): str(v) for k, v in headers_raw.items()}\n\n        timeout_value = mapping.get(\"timeout\")\n        timeout: float | None\n        if timeout_value is None:\n            timeout = None\n        elif isinstance(timeout_value, (int, float)):\n            timeout = float(timeout_value)\n        else:\n            raise ConfigError(\"timeout must be numeric\", extend_path(path, \"timeout\"))\n\n        cache_ttl_value = mapping.get(\"cache_ttl\", 0.0)\n        if cache_ttl_value is None:\n            cache_ttl = 0.0\n        elif isinstance(cache_ttl_value, (int, float)):\n            cache_ttl = float(cache_ttl_value)\n        else:\n            raise ConfigError(\"cache_ttl must be numeric\", extend_path(path, \"cache_ttl\"))\n\n        tool_sources_raw = mapping.get(\"tool_sources\")\n        tool_sources: List[str] | None = None\n        if tool_sources_raw is not None:\n            entries = ensure_list(tool_sources_raw)\n            normalized: List[str] = []\n            for idx, entry in enumerate(entries):\n                if not isinstance(entry, str):\n                    raise ConfigError(\n                        \"tool_sources must be a list of strings\",\n                        extend_path(path, f\"tool_sources[{idx}]\"),\n                    )\n                value = entry.strip()\n                if value:\n                    normalized.append(value)\n            tool_sources = normalized\n        else:\n            tool_sources = [\"mcp_tools\"]","sourceCodeStart":369,"sourceCodeEnd":405,"githubUrl":"https://github.com/OpenBMB/ChatDev/blob/4fb2db0ea90375ce1059f44fe03ffbd191a7a169/entity/configs/node/tooling.py#L369-L405","documentation":"Raised when the 'cache_ttl' key of a tooling config is present but not numeric (int/float accepted). cache_ttl controls how long tool results are cached; the deserializer refuses non-numeric values rather than guessing.","triggerScenarios":"Tooling config mapping with cache_ttl: \"60\" or cache_ttl: [60] passed to from_dict; YAML files where the TTL is quoted.","commonSituations":"Copy-pasted example configs with quoted numbers; environment-variable interpolation that yields strings (cache_ttl: ${TOOL_TTL}).","solutions":["Make cache_ttl a number (60 or 0.5) or drop it / set null for the 0.0 default","Coerce with float() before from_dict if the value is externally sourced","Add schema validation for numeric fields in your config pipeline"],"exampleFix":"# before\ncache_ttl: \"60\"\n# after\ncache_ttl: 60","handlingStrategy":"validation","validationCode":"v = cfg.get(\"cache_ttl\", 0.0)\nassert v is None or isinstance(v, (int, float)) and not isinstance(v, bool)","typeGuard":"def valid_ttl(v) -> bool:\n    return v is None or (isinstance(v, (int, float)) and not isinstance(v, bool))","tryCatchPattern":"try:\n    obj = from_dict(mapping)\nexcept ConfigError as e:\n    log_config_error(e)  # message includes JSON path to cache_ttl","preventionTips":["Quote nothing that must be a number","Centralize config sanitization before from_dict"],"tags":["config","validation","cache","tooling"],"backgroundTag":"config-type-validation-failed","analyzedSha":"4fb2db0ea90375ce1059f44fe03ffbd191a7a169","analyzedAt":"2026-08-27T14:35:29.622Z","schemaVersion":2},"datasetVersion":"2026-08-27T19:17:21.184Z"}