{"record":{"id":"f57a3cfff2c11874","repo":"ZhuLinsen/daily_stock_analysis","slug":"invalid-rss-atom-feed-exc","errorCode":null,"errorMessage":"invalid RSS/Atom feed: {exc}","messagePattern":"invalid RSS/Atom feed: (.+?)","errorType":"exception","errorClass":"IntelligenceServiceError","httpStatus":400,"severity":"warning","filePath":"src/services/intelligence_service.py","lineNumber":600,"sourceCode":"            return normalized.encode(\"idna\").decode(\"ascii\")\n        except UnicodeError:\n            return normalized\n\n    @staticmethod\n    def _validate_addrinfos(addr_infos: Any) -> None:\n        for info in addr_infos or []:\n            try:\n                ip = ipaddress.ip_address(info[4][0])\n            except (IndexError, TypeError, ValueError):\n                continue\n            if IntelligenceService._is_blocked_ip(ip):\n                raise IntelligenceServiceError(\"source url must not target private or local network addresses\")\n\n    def _parse_feed(self, content: bytes, *, source_name: str, limit: int) -> List[FeedEntry]:\n        try:\n            root = ET.fromstring(content)\n        except ET.ParseError as exc:\n            raise IntelligenceServiceError(f\"invalid RSS/Atom feed: {exc}\") from exc\n        tag = self._strip_ns(root.tag).lower()\n        if tag == \"rss\":\n            nodes = root.findall(\"./channel/item\")\n            return [entry for entry in (self._parse_rss_item(node, source_name) for node in nodes[:limit]) if entry]\n        if tag == \"feed\":\n            nodes = root.findall(\"./{*}entry\") or root.findall(\"./entry\")\n            return [entry for entry in (self._parse_atom_entry(node, source_name) for node in nodes[:limit]) if entry]\n        raise IntelligenceServiceError(\"unsupported feed format; expected RSS or Atom\")\n\n    def _parse_newsnow_payload(self, payload: Any, *, source_name: str, limit: int) -> List[FeedEntry]:\n        if not isinstance(payload, dict):\n            raise IntelligenceServiceError(\"invalid NewsNow response: expected object\")\n        items = payload.get(\"items\")\n        if not isinstance(items, list):\n            raise IntelligenceServiceError(\"invalid NewsNow response: missing items\")\n        entries = []\n        for item in items[:limit]:\n            if not isinstance(item, dict):","sourceCodeStart":582,"sourceCodeEnd":618,"githubUrl":"https://github.com/ZhuLinsen/daily_stock_analysis/blob/5159bd72e8373d215492dff122acc9d389e219c9/src/services/intelligence_service.py#L582-L618","documentation":"IntelligenceServiceError('invalid RSS/Atom feed: {exc}') is raised by _parse_feed when xml.etree.ElementTree.fromstring fails with ET.ParseError on the downloaded feed body. The message embeds the underlying parser message (e.g. 'syntax error', 'mismatched tag', 'not well-formed (invalid token)'). It means the bytes handed to the XML parser were not well-formed XML.","triggerScenarios":"A feed URL returning HTML (login pages, Cloudflare challenges, 404 pages served with status 200), truncated bodies from proxies, feeds containing unescaped ampersands or invalid UTF-8, or a payload that exceeded some downstream truncation before parsing.","commonSituations":"Source behind authentication that redirects to an HTML login, CDN bot protection returning an interstitial, an RSS generator emitting unescaped entities, charset mismatch (GBK content declared as UTF-8), or a truncated response from an aggressive proxy.","solutions":["Fetch the URL manually and inspect the first bytes: curl -sL <url> | head -c 400 — HTML output means the URL/auth is wrong, not the parser","If the body is HTML, fix the source configuration (real feed URL, required headers, or cookies)","If the body is XML but malformed, report it upstream or switch to the provider's valid feed endpoint","Validate encoding: ensure the response is decoded per its declared charset before ET.fromstring","Catch IntelligenceServiceError per source so one bad feed does not abort the whole refresh"],"exampleFix":"# before: URL actually serves an HTML portal page\nsource = IntelligenceSource(name=\"news\", url=\"https://example.com/news\")\n\n# after: the real RSS endpoint\nsource = IntelligenceSource(name=\"news\", url=\"https://example.com/news/rss.xml\")","handlingStrategy":"try-catch","validationCode":"def looks_like_feed_xml(content: bytes) -> bool:\n    head = content.lstrip()[:256]\n    return head.startswith(b\"<?xml\") or b\"<rss\" in head or b\"<feed\" in head","typeGuard":null,"tryCatchPattern":"try:\n    entries = svc._parse_feed(content, source_name=src, limit=20)\nexcept IntelligenceServiceError as exc:\n    if \"invalid RSS/Atom feed\" in str(exc):\n        logger.warning(\"source %s returned non-XML body; check auth/URL\", src)\n        entries = []  # degrade per source","preventionTips":["Sniff the first bytes for '<?xml'/'<rss'/'<feed' before parsing","Log a content preview (first 200 bytes) on parse failure to diagnose HTML error pages","Monitor feed sources for auth expiry and bot-challenge pages"],"tags":["network","feed","xml","parsing","rss"],"backgroundTag":null,"analyzedSha":"5159bd72e8373d215492dff122acc9d389e219c9","analyzedAt":"2026-08-15T01:59:36.292Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}