{"record":{"id":"8ade08235dacbb02","repo":"sansan0/TrendRadar","slug":"param-name","errorCode":null,"errorMessage":"{param_name} 必须是数字类型","messagePattern":"(.+?) 必须是数字类型","errorType":"validation","errorClass":"InvalidParameterError","httpStatus":null,"severity":"error","filePath":"mcp_server/utils/validators.py","lineNumber":614,"sourceCode":"    Returns:\n        验证后的阈值\n\n    Raises:\n        InvalidParameterError: 参数无效\n    \"\"\"\n    if threshold is None:\n        return default\n\n    # 支持字符串形式的数字（某些 MCP 客户端会将数字序列化为字符串）\n    if isinstance(threshold, str):\n        threshold = _parse_string_to_float(threshold, param_name)\n\n    # 整数转浮点数\n    if isinstance(threshold, int):\n        threshold = float(threshold)\n\n    if not isinstance(threshold, float):\n        raise InvalidParameterError(\n            f\"{param_name} 必须是数字类型\",\n            suggestion=f\"请提供 {min_value} 到 {max_value} 之间的数字\"\n        )\n\n    if threshold < min_value or threshold > max_value:\n        raise InvalidParameterError(\n            f\"{param_name} 必须在 {min_value} 到 {max_value} 之间，当前值: {threshold}\",\n            suggestion=f\"推荐值: {default}\"\n        )\n\n    return threshold\n\n\ndef validate_date_query(\n    date_query: str,\n    allow_future: bool = False,\n    max_days_ago: int = 365\n) -> datetime:","sourceCodeStart":596,"sourceCodeEnd":632,"githubUrl":"https://github.com/sansan0/TrendRadar/blob/8ee26026ba6c11dec41a95fb3895a7162876caa1/mcp_server/utils/validators.py#L596-L632","documentation":"Raised by the numeric threshold validator when the value is neither None, int, float, nor a string parseable to float. The validator intentionally accepts ints and numeric strings (some MCP clients serialize numbers as strings), converting both to float; anything else (list, dict, bool edge cases, non-numeric strings that survived _parse_string_to_float) fails here with a range suggestion.","triggerScenarios":"Passing threshold=[0.5], threshold={\"value\":0.5}, or threshold=None handled earlier — concretely, any non-None value that is not int/float and not a string that _parse_string_to_float could parse. Example: threshold=\"high\" where the string parser rejects or the value is a container.","commonSituations":"LLM tool callers wrapping thresholds in quotes-with-units (\"0.8 or 80%\"), nesting the value one level too deep in JSON, or passing booleans where the parser does not accept them.","solutions":["Pass a bare number: threshold=0.8.","If the client only sends strings, use a plain numeric string like \"0.8\" (no units, no percent signs).","Unwrap nested JSON — send the number itself, not {\"value\": 0.8}.","Consult the suggestion for the accepted [min_value, max_value] range."],"exampleFix":"// before\ntool_call(threshold={\"value\": 0.8})\ntool_call(threshold=\"80%\")\n\n// after\ntool_call(threshold=0.8)","handlingStrategy":"type-guard","validationCode":"def coerce_threshold(v):\n    if v is None:\n        return None\n    if isinstance(v, bool):\n        raise TypeError(\"threshold must be numeric, not bool\")\n    if isinstance(v, (int, float)):\n        return float(v)\n    if isinstance(v, str):\n        return float(v.strip())  # raises ValueError on garbage\n    raise TypeError(f\"threshold must be number or numeric string, got {type(v).__name__}\")","typeGuard":"def is_numeric_threshold(v) -> bool:\n    if v is None or isinstance(v, bool):\n        return v is None\n    if isinstance(v, (int, float)):\n        return True\n    if isinstance(v, str):\n        try:\n            float(v.strip())\n            return True\n        except ValueError:\n            return False\n    return False","tryCatchPattern":"try:\n    tool_call(threshold=t)\nexcept InvalidParameterError as e:\n    if \"必须是数字类型\" in str(e):\n        t = float(str(t).strip().rstrip('%')) / (100 if '%' in str(t) else 1)\n        tool_call(threshold=t)","preventionTips":["Send bare JSON numbers, not nested objects or strings with units.","Strip '%' and rescale percentages to fractions before sending."],"tags":["validation","mcp","type-error","numeric"],"backgroundTag":null,"analyzedSha":"8ee26026ba6c11dec41a95fb3895a7162876caa1","analyzedAt":"2026-08-15T01:42:18.084Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}