sansan0/TrendRadar · error · InvalidParameterError

{param_name} 必须是数字类型

Error message

{param_name} 必须是数字类型

What it means

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.

Source

Thrown at mcp_server/utils/validators.py:614

    Returns:
        验证后的阈值

    Raises:
        InvalidParameterError: 参数无效
    """
    if threshold is None:
        return default

    # 支持字符串形式的数字(某些 MCP 客户端会将数字序列化为字符串)
    if isinstance(threshold, str):
        threshold = _parse_string_to_float(threshold, param_name)

    # 整数转浮点数
    if isinstance(threshold, int):
        threshold = float(threshold)

    if not isinstance(threshold, float):
        raise InvalidParameterError(
            f"{param_name} 必须是数字类型",
            suggestion=f"请提供 {min_value} 到 {max_value} 之间的数字"
        )

    if threshold < min_value or threshold > max_value:
        raise InvalidParameterError(
            f"{param_name} 必须在 {min_value} 到 {max_value} 之间,当前值: {threshold}",
            suggestion=f"推荐值: {default}"
        )

    return threshold


def validate_date_query(
    date_query: str,
    allow_future: bool = False,
    max_days_ago: int = 365
) -> datetime:

View on GitHub (pinned to 8ee26026ba)

Solutions

  1. Pass a bare number: threshold=0.8.
  2. If the client only sends strings, use a plain numeric string like "0.8" (no units, no percent signs).
  3. Unwrap nested JSON — send the number itself, not {"value": 0.8}.
  4. Consult the suggestion for the accepted [min_value, max_value] range.

Example fix

// before
tool_call(threshold={"value": 0.8})
tool_call(threshold="80%")

// after
tool_call(threshold=0.8)
Defensive patterns

Strategy: type-guard

Validate before calling

def coerce_threshold(v):
    if v is None:
        return None
    if isinstance(v, bool):
        raise TypeError("threshold must be numeric, not bool")
    if isinstance(v, (int, float)):
        return float(v)
    if isinstance(v, str):
        return float(v.strip())  # raises ValueError on garbage
    raise TypeError(f"threshold must be number or numeric string, got {type(v).__name__}")

Type guard

def is_numeric_threshold(v) -> bool:
    if v is None or isinstance(v, bool):
        return v is None
    if isinstance(v, (int, float)):
        return True
    if isinstance(v, str):
        try:
            float(v.strip())
            return True
        except ValueError:
            return False
    return False

Try / catch

try:
    tool_call(threshold=t)
except InvalidParameterError as e:
    if "必须是数字类型" in str(e):
        t = float(str(t).strip().rstrip('%')) / (100 if '%' in str(t) else 1)
        tool_call(threshold=t)

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of sansan0/TrendRadar@8ee26026ba (2026-08-15). Data as JSON: /api/errors/8ade08235dacbb02. Report an issue: GitHub.