crewAIInc/crewAI · error · ValueError

seconds must be zero or greater, got {seconds:g}.

Error message

seconds must be zero or greater, got {seconds:g}.

What it means

WaitTool's duration validator rejects negative wait times. A negative duration is meaningless for sleep() and usually signals a sign error or misparsed value upstream, so it fails fast with a message echoing the offending number (%g formatting).

Source

Thrown at lib/crewai-tools/src/crewai_tools/tools/wait_tool/wait_tool.py:177

        ``BaseTool.run`` skips ``args_schema`` validation when called with
        positional arguments, so the bounds are enforced here too rather than
        left to ``time.sleep`` to reject. Infinity is a valid request: it clamps
        to the cap like any other oversized wait.

        Args:
            seconds: The requested wait duration.

        Returns:
            A tuple of the duration to actually wait and whether it was capped.

        Raises:
            ValueError: If ``seconds`` is negative or not a number.
        """
        if math.isnan(seconds):
            raise ValueError("seconds must be a number, got NaN.")
        if seconds < 0:
            raise ValueError(f"seconds must be zero or greater, got {seconds:g}.")
        if seconds > self.max_seconds:
            return self.max_seconds, True
        return seconds, False

    def _format_result(
        self, waited: float, requested: float, reason: str | None
    ) -> str:
        """Describe the completed wait back to the model.

        Args:
            waited: The duration actually waited.
            requested: The duration the model asked for.
            reason: Optional note on what is being waited for.

        Returns:
            A summary of how long was waited and whether the request was capped.
        """
        parts = [f"Waited {_format_seconds(waited)}."]

View on GitHub (pinned to 754d7323be)

Solutions

  1. Clamp to zero (or a small positive floor) before calling: seconds = max(0.0, seconds).
  2. Fix the sign logic upstream (swap operands of the subtraction, use abs() only if direction is irrelevant).
  3. Add a minimum/maximum constraint in the tool description so the model is steered to non-negative values.

Example fix

# before
seconds = end - start  # accidentally negative
tool.run(seconds=seconds)  # ValueError: must be zero or greater

# after
seconds = max(0.0, end - start)
tool.run(seconds=seconds)
Defensive patterns

Strategy: validation

Validate before calling

def clamp_seconds(v) -> float:
    try:
        f = float(v)
    except (TypeError, ValueError):
        return 0.0
    return max(0.0, f)

# seconds = clamp_seconds(raw) before WaitTool

Type guard

import math

def is_non_negative_number(v) -> bool:
    try:
        f = float(v)
    except (TypeError, ValueError):
        return False
    return not math.isnan(f) and f >= 0

Try / catch

try:
    tool.run(seconds=raw)
except ValueError as e:
    if 'zero or greater' in str(e):
        tool.run(seconds=abs(float(raw)))  # or 0.0; log the sign error
    else:
        raise

Prevention

When it happens

Trigger: An agent or caller invoking wait with a negative number (wait(seconds=-5)), or arithmetic that yields a negative value such as end_time - start_time with the operands swapped, or '-30' parsed from model output.

Common situations: LLMs emitting negative durations in tool calls; time-delta math where a countdown goes below zero; timezone/DST arithmetic producing negative intervals.

Related errors


AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15). Data as JSON: /api/errors/b567a24402b5d2ca. Report an issue: GitHub.