PrefectHQ/fastmcp · error · TypeError
Invalid timeout type: {type(value)}
Error message
Invalid timeout type: {type(value)} What it means
normalize_timeout_to_timedelta converts timeout settings into datetime.timedelta, accepting None, timedelta, or int/float seconds. Any other type (str, None-like objects, Decimal, etc.) raises TypeError naming the offending type. Zero/None semantics are handled; only the type is policed here.
Source
Thrown at fastmcp_slim/fastmcp/utilities/timeout.py:25
def normalize_timeout_to_timedelta(
value: int | float | datetime.timedelta | None,
) -> datetime.timedelta | None:
"""Normalize a timeout value to a timedelta.
Args:
value: Timeout value as int/float (seconds), timedelta, or None
Returns:
timedelta if value provided, None otherwise
"""
if value is None:
return None
if isinstance(value, datetime.timedelta):
return value
if isinstance(value, int | float):
return datetime.timedelta(seconds=float(value))
raise TypeError(f"Invalid timeout type: {type(value)}")
def normalize_timeout_to_seconds(
value: int | float | datetime.timedelta | None,
) -> float | None:
"""Normalize a timeout value to seconds (float).
Args:
value: Timeout value as int/float (seconds), timedelta, or None.
Zero values are treated as "disabled" and return None.
Returns:
float seconds if value provided and non-zero, None otherwise
"""
if value is None:
return None
if isinstance(value, datetime.timedelta):
seconds = value.total_seconds()View on GitHub (pinned to 1f02114297)
Solutions
- Convert to a numeric type or timedelta before passing: float(value) or datetime.timedelta(seconds=...)
- Parse config/env strings explicitly: timeout = float(os.environ["TIMEOUT"])
- Pass None for 'no timeout' rather than a string like "none"
Example fix
// before
component = Tool(..., task_config={"timeout": "30"})
// after
component = Tool(..., task_config={"timeout": 30}) # or datetime.timedelta(seconds=30) Defensive patterns
Strategy: type-guard
Validate before calling
def coerce_timeout(value) -> datetime.timedelta | None:
if value is None or isinstance(value, (datetime.timedelta, int, float)):
return value if not isinstance(value, str) else float(value)
if isinstance(value, str):
return float(value)
raise TypeError(f"unsupported timeout: {type(value)}") Type guard
def is_valid_timeout(v) -> bool:
return v is None or isinstance(v, (datetime.timedelta, int, float)) and not isinstance(v, bool) Try / catch
try:
comp = Tool(..., task_config={"timeout": raw_timeout})
except TypeError as e:
if "Invalid timeout type" in str(e):
raw_timeout = float(raw_timeout) # strings from env/config
else:
raise Prevention
- Parse env/config timeouts with float() at load time
- Use typed config models (pydantic) for durations
- Never pass string durations like "30s" to numeric timeout fields
When it happens
Trigger: Passing a timeout as a string ("30"), Decimal, or other non-numeric type to a component/constructor that normalizes via this function (e.g. timeout="30" in task configuration).
Common situations: Reading timeout values from environment variables or config files where they arrive as strings without conversion; JSON configs producing strings; mixing up seconds vs timedelta in typed APIs.
Understand the failure class
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- Protocol mode for server {name!r} must be a string
- Failed to initialize server session
- ClientGroup requires at least one client
- mcp_tool() got unexpected keyword argument(s): {sorted(unkno
- mcp_resource() got unexpected keyword argument(s): {sorted(u
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/fb99b610d7a40c42.
Report an issue: GitHub.