{"record":{"id":"871e019e5f149b4a","repo":"OpenBB-finance/OpenBB","slug":"invalid-format-for-urls-must-be-str-dict-or-lis","errorCode":null,"errorMessage":"Invalid format for URLs. Must be str, dict, or list.","messagePattern":"Invalid format for URLs\\. Must be str, dict, or list\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"openbb_platform/core/openbb_core/provider/standard_models/weather_bulletin_download.py","lineNumber":28,"sourceCode":"\n    urls: str | dict | list = Field(\n        kw_only=True,\n        description=\"URLs for reports to download.\",\n    )\n\n    @field_validator(\"urls\", mode=\"before\", check_fields=False)\n    @classmethod\n    def _validate_urls(cls, v):\n        \"\"\"Validate URLs input.\"\"\"\n        if isinstance(v, str):\n            if \",\" in v:\n                return v.split(\",\")\n            return [v]\n        if isinstance(v, dict) and \"urls\" in v:\n            return v[\"urls\"]\n        if isinstance(v, list):\n            return v\n        raise ValueError(\"Invalid format for URLs. Must be str, dict, or list.\")\n\n\nclass WeatherBulletinDownloadData(Data):\n    \"\"\"Weather Bulletin Data.\"\"\"\n\n    content: str = Field(\n        description=\"Base64 encoded content of the weather bulletin document.\",\n    )\n","sourceCodeStart":10,"sourceCodeEnd":37,"githubUrl":"https://github.com/OpenBB-finance/OpenBB/blob/3e071fcc2cd9f891cac6040ae60296dba76dab46/openbb_platform/core/openbb_core/provider/standard_models/weather_bulletin_download.py#L10-L37","documentation":"Raised by the Pydantic field validator on the 'urls' field of the WeatherBulletinDownloadQueryParams model (weather_bulletin_download.py). The validator normalizes a str (comma-split), a dict containing an 'urls' key, or a list into a list of URLs; any other Python type (int, tuple, None, set) raises this ValueError and fails parameter validation.","triggerScenarios":"Calling obb.economy.weather.bulletin.download (or a provider fetcher reusing this QueryParams) with urls passed as a tuple, a dict without an 'urls' key, a plain number, or None. Also passing a nested dict like {'urls': 123} passes the dict branch but then fails downstream Pydantic list validation.","commonSituations":"Building the urls argument dynamically from another API that returns tuples/sets; sending JSON where urls is null; copy-pasting a single URL wrapped in parentheses (tuple) instead of brackets (list).","solutions":["Pass urls as a list of strings: urls=['https://...pdf']","Or a comma-separated string for multiple URLs: urls='https://a.pdf,https://b.pdf'","Or a dict with the exact key 'urls': urls={'urls': [...]}; note the key must literally be 'urls'","If the value comes from user input/JSON, coerce to list before the call: urls=list(value) when isinstance(value, (list, tuple, set))"],"exampleFix":"# before\nres = await obb.economy.weather.bulletin.download(urls=(\"https://example.com/bulletin.pdf\",))  # tuple -> ValueError\n\n# after\nres = await obb.economy.weather.bulletin.download(urls=[\"https://example.com/bulletin.pdf\"])  # list","handlingStrategy":"validation","validationCode":"def normalize_weather_urls(v) -> list[str]:\n    if isinstance(v, str):\n        return v.split(\",\")\n    if isinstance(v, dict) and \"urls\" in v:\n        return list(v[\"urls\"])\n    if isinstance(v, (list, tuple)):\n        return [str(u) for u in v]\n    raise TypeError(f\"urls must be str/dict/list, got {type(v).__name__}\")\n\nurls = normalize_weather_urls(raw_urls)","typeGuard":"from typing import Any\n\ndef is_valid_urls_input(v: Any) -> bool:\n    return (\n        isinstance(v, str)\n        or (isinstance(v, dict) and \"urls\" in v)\n        or (isinstance(v, list) and all(isinstance(u, str) for u in v))\n    )","tryCatchPattern":"from pydantic import ValidationError\n\ntry:\n    res = await obb.economy.weather.bulletin.download(urls=urls)\nexcept ValidationError as e:\n    bad = [err for err in e.errors() if err[\"loc\"] == (\"urls\",)]\n    if bad:\n        urls = [str(urls)]  # last-resort coercion\n        res = await obb.economy.weather.bulletin.download(urls=urls)","preventionTips":["Always pass urls as a list of strings; treat str/dict as convenience forms only","When receiving JSON from users, validate urls with a small schema (must be array of URL strings) before forwarding","Watch out for JSON that decodes tuples as lists elsewhere but tuples in Python when calling the SDK directly"],"tags":["pydantic","validation","weather","query-params"],"backgroundTag":null,"analyzedSha":"3e071fcc2cd9f891cac6040ae60296dba76dab46","analyzedAt":"2026-08-14T23:40:48.960Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}