{"record":{"id":"48579e28673099c2","repo":"ZhuLinsen/daily_stock_analysis","slug":"0","errorCode":null,"errorMessage":"题材新闻搜索超时必须大于 0 秒","messagePattern":"题材新闻搜索超时必须大于 0 秒","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"src/search_service.py","lineNumber":3961,"sourceCode":"        max_results: int = 5,\n        focus_keywords: Optional[List[str]] = None,\n        *,\n        timeout_seconds: float = 12.0,\n    ) -> SearchResponse:\n        \"\"\"Search topic news within one cache-wait and provider deadline.\"\"\"\n        topic_text = (topic or \"\").strip()\n        if not topic_text or not self.is_available:\n            return SearchResponse(\n                query=topic_text,\n                results=[],\n                provider=\"None\",\n                success=False,\n                error_message=\"未配置搜索能力或题材为空\",\n            )\n\n        wait_seconds = float(timeout_seconds)\n        if wait_seconds <= 0:\n            raise ValueError(\"题材新闻搜索超时必须大于 0 秒\")\n        deadline = time.monotonic() + wait_seconds\n        search_days = self._effective_news_window_days()\n        query_terms = [str(item).strip() for item in (focus_keywords or []) if str(item).strip()]\n        query = \" \".join(query_terms) if query_terms else f'\"{topic_text}\" A股 最新消息 催化'\n        cache_key = self._cache_key(f\"topic_news:{topic_text}:{query}\", max_results, search_days)\n        cached, cache_owner, cache_event, _waited = self._get_cached_or_wait_for_reservation(\n            cache_key,\n            deadline=deadline,\n        )\n        if cached is not None:\n            return cached\n\n        try:\n            remaining = deadline - time.monotonic()\n            if remaining <= 0:\n                raise TimeoutError(\"题材新闻搜索等待超过调用截止时间\")\n            response = _call_topic_news_in_subprocess(\n                constructor_kwargs=self._constructor_kwargs,","sourceCodeStart":3943,"sourceCodeEnd":3979,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/search_service.py#L3943-L3979","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Pass a positive timeout in seconds, e.g. timeout_seconds=30.","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.","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.","Audit the call site for unit confusion (ms vs s) and convert explicitly."],"exampleFix":"// before\nresponse = search_service.search_topic_news(\n    topic=\"机器人\", timeout_seconds=0  # meant 'no limit'\n)\n\n// after\nresponse = search_service.search_topic_news(\n    topic=\"机器人\", timeout_seconds=30.0\n)","handlingStrategy":"validation","validationCode":"timeout = float(timeout_seconds)\nif not (timeout > 0) or timeout != timeout:\n    raise ValueError('configure a positive topic-news timeout')\nresponse = svc.search_topic_news(topic=topic, timeout_seconds=timeout)","typeGuard":"def is_positive_timeout(v) -> bool:\n    try:\n        return float(v) > 0\n    except (TypeError, ValueError):\n        return False","tryCatchPattern":"try:\n    resp = svc.search_topic_news(topic=t, timeout_seconds=ts)\nexcept ValueError as e:\n    if '超时必须大于 0' in str(e):\n        ts = 30.0  # fall back to a sane default and retry once\n        resp = svc.search_topic_news(topic=t, timeout_seconds=ts)\n    else:\n        raise","preventionTips":["Never use 0 as a 'no timeout' sentinel for this API; omit or use a positive default.","Centralize timeout constants in config with positive-value validation.","Unit-test the call path with timeout_seconds in {0, -1, '0.0'} to catch regressions early."],"tags":["validation","timeout","search","config"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}