{"record":{"id":"1f685bab6a21d306","repo":"crewAIInc/crewAI","slug":"seconds-must-be-a-number-got-nan","errorCode":null,"errorMessage":"seconds must be a number, got NaN.","messagePattern":"seconds must be a number, got NaN\\.","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"lib/crewai-tools/src/crewai_tools/tools/wait_tool/wait_tool.py","lineNumber":175,"sourceCode":"    def _resolve_duration(self, seconds: float) -> tuple[float, bool]:\n        \"\"\"Validate and clamp the requested duration to ``max_seconds``.\n\n        ``BaseTool.run`` skips ``args_schema`` validation when called with\n        positional arguments, so the bounds are enforced here too rather than\n        left to ``time.sleep`` to reject. Infinity is a valid request: it clamps\n        to the cap like any other oversized wait.\n\n        Args:\n            seconds: The requested wait duration.\n\n        Returns:\n            A tuple of the duration to actually wait and whether it was capped.\n\n        Raises:\n            ValueError: If ``seconds`` is negative or not a number.\n        \"\"\"\n        if math.isnan(seconds):\n            raise ValueError(\"seconds must be a number, got NaN.\")\n        if seconds < 0:\n            raise ValueError(f\"seconds must be zero or greater, got {seconds:g}.\")\n        if seconds > self.max_seconds:\n            return self.max_seconds, True\n        return seconds, False\n\n    def _format_result(\n        self, waited: float, requested: float, reason: str | None\n    ) -> str:\n        \"\"\"Describe the completed wait back to the model.\n\n        Args:\n            waited: The duration actually waited.\n            requested: The duration the model asked for.\n            reason: Optional note on what is being waited for.\n\n        Returns:\n            A summary of how long was waited and whether the request was capped.","sourceCodeStart":157,"sourceCodeEnd":193,"githubUrl":"https://github.com/crewAIInc/crewAI/blob/754d7323beb2fd042e33444a115ea2d5a47193f0/lib/crewai-tools/src/crewai_tools/tools/wait_tool/wait_tool.py#L157-L193","documentation":"WaitTool normalizes requested wait durations through a validation helper. It explicitly rejects NaN (math.isnan) because NaN comparisons are always False, so an uncapped NaN would silently pass the max_seconds cap check; the ValueError names the exact defect.","triggerScenarios":"An LLM emitting a tool call like wait(seconds='NaN') that parses to float('nan'), or user code computing seconds as 0/0, float('nan'), or a NaN from math operations (math.inf - math.inf) and passing it to WaitTool.","commonSituations":"Model-generated JSON with the literal token NaN (accepted by Python's float() but invalid strict JSON); arithmetic upstream producing NaN (division by zero in a stat that feeds the wait); NaN leaking through pandas/numpy values passed unconverted.","solutions":["Sanitize numeric inputs before calling the tool: use a helper that rejects/replaces NaN (e.g., 0.0) with math.isnan.","Fix the upstream computation producing NaN (guard zero divisors, fillna() in pandas).","Tighten the tool schema/format so model output cannot be NaN (strict JSON parsing: json.loads rejects NaN unless parse_constant allows it)."],"exampleFix":"# before\nimport math\nseconds = float('nan')  # e.g. from model output 'NaN'\ntool.run(seconds=seconds)  # ValueError: got NaN\n\n# after\nseconds = seconds if (seconds is not None and not math.isnan(seconds)) else 0.0\ntool.run(seconds=seconds)","handlingStrategy":"validation","validationCode":"import math\n\ndef safe_seconds(v) -> float:\n    try:\n        f = float(v)\n    except (TypeError, ValueError):\n        return 0.0\n    return 0.0 if math.isnan(f) else f\n\n# seconds = safe_seconds(model_output) before WaitTool","typeGuard":"import math\n\ndef is_valid_seconds(v) -> bool:\n    try:\n        f = float(v)\n    except (TypeError, ValueError):\n        return False\n    return not math.isnan(f) and f >= 0","tryCatchPattern":"try:\n    tool.run(seconds=raw)\nexcept ValueError as e:\n    if 'NaN' in str(e):\n        tool.run(seconds=0.0)  # sane default, log the bad input\n    else:\n        raise","preventionTips":["Sanitize all model-provided numerics through float() + isnan/isinf checks.","Use strict JSON parsing (json.loads rejects NaN literals).","Centralize numeric coercion in one helper used by every tool boundary."],"tags":["validation","nan","numeric","llm-output"],"backgroundTag":null,"analyzedSha":"754d7323beb2fd042e33444a115ea2d5a47193f0","analyzedAt":"2026-08-15T04:06:56.746Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}