OpenBB-finance/OpenBB · error · ValueError

Invalid format for URLs. Must be str, dict, or list.

Error message

Invalid format for URLs. Must be str, dict, or list.

What it means

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.

Source

Thrown at openbb_platform/core/openbb_core/provider/standard_models/weather_bulletin_download.py:28

    urls: str | dict | list = Field(
        kw_only=True,
        description="URLs for reports to download.",
    )

    @field_validator("urls", mode="before", check_fields=False)
    @classmethod
    def _validate_urls(cls, v):
        """Validate URLs input."""
        if isinstance(v, str):
            if "," in v:
                return v.split(",")
            return [v]
        if isinstance(v, dict) and "urls" in v:
            return v["urls"]
        if isinstance(v, list):
            return v
        raise ValueError("Invalid format for URLs. Must be str, dict, or list.")


class WeatherBulletinDownloadData(Data):
    """Weather Bulletin Data."""

    content: str = Field(
        description="Base64 encoded content of the weather bulletin document.",
    )

View on GitHub (pinned to 3e071fcc2c)

Solutions

  1. Pass urls as a list of strings: urls=['https://...pdf']
  2. Or a comma-separated string for multiple URLs: urls='https://a.pdf,https://b.pdf'
  3. Or a dict with the exact key 'urls': urls={'urls': [...]}; note the key must literally be 'urls'
  4. If the value comes from user input/JSON, coerce to list before the call: urls=list(value) when isinstance(value, (list, tuple, set))

Example fix

# before
res = await obb.economy.weather.bulletin.download(urls=("https://example.com/bulletin.pdf",))  # tuple -> ValueError

# after
res = await obb.economy.weather.bulletin.download(urls=["https://example.com/bulletin.pdf"])  # list
Defensive patterns

Strategy: validation

Validate before calling

def normalize_weather_urls(v) -> list[str]:
    if isinstance(v, str):
        return v.split(",")
    if isinstance(v, dict) and "urls" in v:
        return list(v["urls"])
    if isinstance(v, (list, tuple)):
        return [str(u) for u in v]
    raise TypeError(f"urls must be str/dict/list, got {type(v).__name__}")

urls = normalize_weather_urls(raw_urls)

Type guard

from typing import Any

def is_valid_urls_input(v: Any) -> bool:
    return (
        isinstance(v, str)
        or (isinstance(v, dict) and "urls" in v)
        or (isinstance(v, list) and all(isinstance(u, str) for u in v))
    )

Try / catch

from pydantic import ValidationError

try:
    res = await obb.economy.weather.bulletin.download(urls=urls)
except ValidationError as e:
    bad = [err for err in e.errors() if err["loc"] == ("urls",)]
    if bad:
        urls = [str(urls)]  # last-resort coercion
        res = await obb.economy.weather.bulletin.download(urls=urls)

Prevention

When it happens

Trigger: 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.

Common situations: 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).

Related errors


AI-assisted analysis of OpenBB-finance/OpenBB@3e071fcc2c (2026-08-14). Data as JSON: /api/errors/871e019e5f149b4a. Report an issue: GitHub.