OpenBB-finance/OpenBB · error · ValueError

Invalid extension type(s): {', '.join(invalid)}. Valid choic

Error message

Invalid extension type(s): {', '.join(invalid)}. Valid choices: {', '.join(VALID_EXTENSION_TYPES)}

What it means

Raised at market_snapshots.py:195 inside get_csv() when the CSV fetched from a snapshot file URL produces an empty pandas DataFrame. Two paths reach it: (a) the response body was not `bytes` (e.g. an HTML/JSON error page, a dict from an error handler), so `df` stays the empty DataFrame initialized on line 190; or (b) the body was bytes but decompressed/parsed to zero rows. Since asyncio.gather runs get_csv over every file URL concurrently, one bad file fails the whole request.

Source

Thrown at cookiecutter/openbb_cookiecutter/cli.py:27

from rich.prompt import Prompt

from . import get_template_path

VALID_EXTENSION_TYPES = [
    "router",
    "provider",
    "obbject",
    "on_command_output",
    "charting",
    "all",
]


def _parse_extension_types(value: str) -> list[str]:
    types = [t.strip() for t in value.split(",") if t.strip()]
    invalid = [t for t in types if t not in VALID_EXTENSION_TYPES]
    if invalid:
        raise ValueError(
            f"Invalid extension type(s): {', '.join(invalid)}. "
            f"Valid choices: {', '.join(VALID_EXTENSION_TYPES)}"
        )
    if not types:
        raise ValueError("At least one extension type must be selected.")
    return types


def _prompt_context(preset_extension_types: list[str] | None = None) -> dict:
    context = {}

    context["full_name"] = Prompt.ask("  full_name", default="Hello World")
    context["email"] = Prompt.ask("  email", default="hello@world.com")
    context["project_name"] = Prompt.ask(
        "  project_name", default="OpenBB Python Extension Template"
    )
    default_tag = context["project_name"].lower().replace(" ", "-").replace("_", "-")
    context["project_tag"] = Prompt.ask("  project_tag", default=default_tag)

View on GitHub (pinned to 3e071fcc2c)

Solutions

  1. Retry the request end-to-end — the snapshots listing and file URLs are fetched fresh each call, which replaces any expired signed URLs.
  2. Fetch the URL from the erroring file directly (extract snapshots[].files[].url via curl) to see whether the file is genuinely empty or returns an auth/expiry error; if genuinely empty, it is an Intrinio-side issue — report it.
  3. If it recurs for a specific date, request a slightly different datetime (e.g. 30 minutes earlier) to get a different snapshot file set.
  4. Update the provider package (pip install -U openbb-intrinio) in case handling of non-gzip responses was fixed after your version.
  5. Handle transient CDN failures in your own code with a retry/backoff around the whole obb.equity.market_snapshots call.

Example fix

# before
res = obb.equity.market_snapshots(provider='intrinio')  # one empty file kills whole request via asyncio.gather

# after
from openbb_core.app.model.abstract.error import OpenBBError
for attempt in range(3):
    try:
        res = obb.equity.market_snapshots(provider='intrinio')
        break
    except OpenBBError as e:
        if 'Empty CSV file' in str(e) and attempt < 2:
            continue
        raise
Defensive patterns

Strategy: retry

Validate before calling

// not applicable - the empty file is served by Intrinio/S3 and cannot be inspected before the library fetches it; only a retry with a fresh snapshots listing (new signed URLs) helps

Type guard

def is_empty_csv_error(exc: Exception) -> bool:
    """True when a snapshot CSV file came back empty or unparseable."""
    return isinstance(exc, OpenBBError) and 'Empty CSV file' in str(exc)

Try / catch

from openbb_core.app.model.abstract.error import OpenBBError
import asyncio

async def snapshots_with_retry(attempts: int = 3):
    for i in range(attempts):
        try:
            return await obb.equity.market_snapshots(provider='intrinio')
        except OpenBBError as e:
            if 'Empty CSV file' in str(e) and i < attempts - 1:
                await asyncio.sleep(2 ** i)  # signed URL expiry / transient S3 issue
                continue
            raise

Prevention

When it happens

Trigger: Calling `equity.market_snapshots(provider='intrinio')` where at least one snapshot CSV URL returned via response_callback is empty, expired (the signed S3 URLs in the snapshots response are short-lived), redirected to an error body, or served uncompressed/non-gzip content that gzip.decompress would reject. df.empty is also true when read_csv yields only headers with no rows.

Common situations: Reusing a cached/stale snapshots response whose file URLs have expired; transient S3/CDN issue returning an empty or error body; Intrinio snapshot generation producing an empty file for part of the day; race where the snapshot file is still being written; proxy/antivirus stripping the response body.

Related errors


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