ZhuLinsen/daily_stock_analysis · error · ValueError

题材新闻搜索超时必须大于 0 秒

Error message

题材新闻搜索超时必须大于 0 秒

What it means

Raised by SearchService topic-news search (src/search_service.py:3961) when timeout_seconds is parsed as a float and is <= 0. The method enforces a strictly positive, caller-supplied deadline budget before it ever touches cache or subprocess, so a zero/negative timeout is treated as a programming or config error rather than silently running unbounded.

Source

Thrown at src/search_service.py:3961

        max_results: int = 5,
        focus_keywords: Optional[List[str]] = None,
        *,
        timeout_seconds: float = 12.0,
    ) -> SearchResponse:
        """Search topic news within one cache-wait and provider deadline."""
        topic_text = (topic or "").strip()
        if not topic_text or not self.is_available:
            return SearchResponse(
                query=topic_text,
                results=[],
                provider="None",
                success=False,
                error_message="未配置搜索能力或题材为空",
            )

        wait_seconds = float(timeout_seconds)
        if wait_seconds <= 0:
            raise ValueError("题材新闻搜索超时必须大于 0 秒")
        deadline = time.monotonic() + wait_seconds
        search_days = self._effective_news_window_days()
        query_terms = [str(item).strip() for item in (focus_keywords or []) if str(item).strip()]
        query = " ".join(query_terms) if query_terms else f'"{topic_text}" A股 最新消息 催化'
        cache_key = self._cache_key(f"topic_news:{topic_text}:{query}", max_results, search_days)
        cached, cache_owner, cache_event, _waited = self._get_cached_or_wait_for_reservation(
            cache_key,
            deadline=deadline,
        )
        if cached is not None:
            return cached

        try:
            remaining = deadline - time.monotonic()
            if remaining <= 0:
                raise TimeoutError("题材新闻搜索等待超过调用截止时间")
            response = _call_topic_news_in_subprocess(
                constructor_kwargs=self._constructor_kwargs,

View on GitHub (pinned to 5159bd72e8)

Solutions

  1. Pass a positive timeout in seconds, e.g. timeout_seconds=30.
  2. If the timeout comes from config/env, add a sane default (e.g. int(os.getenv('TOPIC_NEWS_TIMEOUT', '30'))) so unset does not become 0.
  3. If the value is a remaining-budget computed from a deadline, check it before calling and skip/handle the exhausted case instead of letting the ValueError fire.
  4. Audit the call site for unit confusion (ms vs s) and convert explicitly.

Example fix

// before
response = search_service.search_topic_news(
    topic="机器人", timeout_seconds=0  # meant 'no limit'
)

// after
response = search_service.search_topic_news(
    topic="机器人", timeout_seconds=30.0
)
Defensive patterns

Strategy: validation

Validate before calling

timeout = float(timeout_seconds)
if not (timeout > 0) or timeout != timeout:
    raise ValueError('configure a positive topic-news timeout')
response = svc.search_topic_news(topic=topic, timeout_seconds=timeout)

Type guard

def is_positive_timeout(v) -> bool:
    try:
        return float(v) > 0
    except (TypeError, ValueError):
        return False

Try / catch

try:
    resp = svc.search_topic_news(topic=t, timeout_seconds=ts)
except ValueError as e:
    if '超时必须大于 0' in str(e):
        ts = 30.0  # fall back to a sane default and retry once
        resp = svc.search_topic_news(topic=t, timeout_seconds=ts)
    else:
        raise

Prevention

When it happens

Trigger: Calling search_topic_news (the topic news entrypoint in src/search_service.py) with timeout_seconds=0, a negative number, or a numeric string like '-1' / '0.0' that float() accepts. Also triggered when a config/env value that feeds timeout_seconds (e.g. a topic-news timeout setting) is unset and coerced to 0.

Common situations: Passing seconds vs milliseconds by mistake (timeout_seconds=500 intended as ms becomes a huge value, but timeout_seconds=0 passed as a 'no timeout' sentinel); wiring an env var like TOPIC_NEWS_TIMEOUT into the call without a default; a caller computing remaining = deadline - now and passing a computed 0 when the budget is already exhausted.

Related errors


AI-assisted analysis of ZhuLinsen/daily_stock_analysis@5159bd72e8 (2026-08-15). Data as JSON: /api/errors/48579e28673099c2. Report an issue: GitHub.