{"record":{"id":"8430eed166b0f799","repo":"BerriAI/litellm","slug":"rss-feed-missing-channel-element","errorCode":null,"errorMessage":"RSS feed missing <channel> element","messagePattern":"RSS feed missing <channel> element","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"litellm/litellm_core_utils/get_blog_posts.py","lineNumber":78,"sourceCode":"        Fetch RSS XML from a remote URL.\n\n        Returns the raw XML text. Raises on network errors.\n        \"\"\"\n        response: Final = httpx.get(url, timeout=timeout)\n        response.raise_for_status()\n        return response.text\n\n    @staticmethod\n    def parse_rss_to_posts(xml_text: str, max_posts: int = 1) -> list[dict[str, str]]:\n        \"\"\"\n        Parse RSS XML and return a list of blog post dicts.\n\n        Extracts title, description, date (YYYY-MM-DD), and url from each <item>.\n        \"\"\"\n        root: Final = ET.fromstring(xml_text)\n        channel: Final = root.find(\"channel\")\n        if channel is None:\n            raise ValueError(\"RSS feed missing <channel> element\")\n\n        posts: Final[list[dict[str, str]]] = []\n        for item in channel.findall(\"item\"):\n            if len(posts) >= max_posts:\n                break\n\n            title_el = item.find(\"title\")\n            link_el = item.find(\"link\")\n            desc_el = item.find(\"description\")\n            pub_date_el = item.find(\"pubDate\")\n\n            if title_el is None or link_el is None:\n                continue\n\n            # Parse RFC 2822 date to YYYY-MM-DD\n            date_str = \"\"\n            if pub_date_el is not None and pub_date_el.text:\n                try:","sourceCodeStart":60,"sourceCodeEnd":96,"githubUrl":"https://github.com/BerriAI/litellm/blob/77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8/litellm/litellm_core_utils/get_blog_posts.py#L60-L96","documentation":"get_llm_provider() (litellm_core_utils/get_llm_provider_logic.py) opens with early validation: if the model argument is None it immediately raises ValueError('model parameter is required but was None. Please provide a valid model name.') before any provider detection runs. Its job is resolving 'azure/foo' or 'openrouter/bar' prefixes to a provider, and None makes that impossible.","triggerScenarios":"litellm.completion(model=None, ...) or Router/get_llm_provider calls where the model string never got set: template/env-driven config leaving it blank, a dict key mismatch (using 'model_name' vs 'model'), or a deployment entry whose model field is absent.","commonSituations":"Dynamic code building the model string that yields None (env var unset, lookup miss); LiteLLM Proxy model_list entries missing 'litellm_params.model'; refactors renaming the variable passed as model; optional model args defaulting to None.","solutions":["Find where the model value originates (env var, config, function arg) and make it a required, validated string.","For Router/proxy configs, ensure every model_list entry has litellm_params.model set to a valid name like 'azure/my-deployment'.","Fail fast at config load: validate all model strings before entering request paths.","Use a sentinel/default model explicitly instead of letting None propagate."],"exampleFix":"# before\nmodel = os.getenv(\"MODEL\")          # None when unset\nlitellm.completion(model=model, messages=msgs)\n\n# after\nmodel = os.environ[\"MODEL\"]          # KeyError at startup, not mid-request\nlitellm.completion(model=model, messages=msgs)","handlingStrategy":"try-catch","validationCode":"import xml.etree.ElementTree as ET\nroot = ET.fromstring(xml_text)\nassert root.find(\"channel\") is not None, \"document is not an RSS 2.0 feed; refusing to parse\"\nposts = RssClient.parse_rss_to_posts(xml_text)","typeGuard":"def is_rss2_feed(xml_text: str) -> bool:\n    try:\n        return ET.fromstring(xml_text).find(\"channel\") is not None\n    except ET.ParseError:\n        return False","tryCatchPattern":"try:\n    posts = RssClient.parse_rss_to_posts(xml_text)\nexcept ValueError as e:\n    if \"missing <channel>\" in str(e):\n        log.warning(\"feed shape changed or was blocked; skipping blog posts\")\n        posts = []\n    else:\n        raise","preventionTips":["Treat blog-post fetching as best-effort: catch ValueError and degrade to no posts.","Check fetched content-type/root tag before parsing external documents.","Allowlist the feed hostname on egress proxies; block pages parse as XML and trip this."],"tags":["validation","missing-model","configuration","litellm","early-validation"],"backgroundTag":"missing-required-argument","analyzedSha":"77b7c6c40c0c5aa5fbcb1d6a1825ac39ca8829b8","analyzedAt":"2026-08-18T11:44:31.656Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-23T08:17:48.524Z"}