{"record":{"id":"bcdcae4ef60362b4","repo":"OpenBB-finance/OpenBB","slug":"invalid-extension-type-s-join-invalid-v","errorCode":null,"errorMessage":"Invalid extension type(s): {', '.join(invalid)}. Valid choices: {', '.join(VALID_EXTENSION_TYPES)}","messagePattern":"Invalid extension type\\(s\\): (.+?)\\. Valid choices: (.+?)","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"cookiecutter/openbb_cookiecutter/cli.py","lineNumber":27,"sourceCode":"from rich.prompt import Prompt\n\nfrom . import get_template_path\n\nVALID_EXTENSION_TYPES = [\n    \"router\",\n    \"provider\",\n    \"obbject\",\n    \"on_command_output\",\n    \"charting\",\n    \"all\",\n]\n\n\ndef _parse_extension_types(value: str) -> list[str]:\n    types = [t.strip() for t in value.split(\",\") if t.strip()]\n    invalid = [t for t in types if t not in VALID_EXTENSION_TYPES]\n    if invalid:\n        raise ValueError(\n            f\"Invalid extension type(s): {', '.join(invalid)}. \"\n            f\"Valid choices: {', '.join(VALID_EXTENSION_TYPES)}\"\n        )\n    if not types:\n        raise ValueError(\"At least one extension type must be selected.\")\n    return types\n\n\ndef _prompt_context(preset_extension_types: list[str] | None = None) -> dict:\n    context = {}\n\n    context[\"full_name\"] = Prompt.ask(\"  full_name\", default=\"Hello World\")\n    context[\"email\"] = Prompt.ask(\"  email\", default=\"hello@world.com\")\n    context[\"project_name\"] = Prompt.ask(\n        \"  project_name\", default=\"OpenBB Python Extension Template\"\n    )\n    default_tag = context[\"project_name\"].lower().replace(\" \", \"-\").replace(\"_\", \"-\")\n    context[\"project_tag\"] = Prompt.ask(\"  project_tag\", default=default_tag)","sourceCodeStart":9,"sourceCodeEnd":45,"githubUrl":"https://github.com/OpenBB-finance/OpenBB/blob/3e071fcc2cd9f891cac6040ae60296dba76dab46/cookiecutter/openbb_cookiecutter/cli.py#L9-L45","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Retry the request end-to-end — the snapshots listing and file URLs are fetched fresh each call, which replaces any expired signed URLs.","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.","If it recurs for a specific date, request a slightly different datetime (e.g. 30 minutes earlier) to get a different snapshot file set.","Update the provider package (pip install -U openbb-intrinio) in case handling of non-gzip responses was fixed after your version.","Handle transient CDN failures in your own code with a retry/backoff around the whole obb.equity.market_snapshots call."],"exampleFix":"# before\nres = obb.equity.market_snapshots(provider='intrinio')  # one empty file kills whole request via asyncio.gather\n\n# after\nfrom openbb_core.app.model.abstract.error import OpenBBError\nfor attempt in range(3):\n    try:\n        res = obb.equity.market_snapshots(provider='intrinio')\n        break\n    except OpenBBError as e:\n        if 'Empty CSV file' in str(e) and attempt < 2:\n            continue\n        raise","handlingStrategy":"retry","validationCode":"// 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","typeGuard":"def is_empty_csv_error(exc: Exception) -> bool:\n    \"\"\"True when a snapshot CSV file came back empty or unparseable.\"\"\"\n    return isinstance(exc, OpenBBError) and 'Empty CSV file' in str(exc)","tryCatchPattern":"from openbb_core.app.model.abstract.error import OpenBBError\nimport asyncio\n\nasync def snapshots_with_retry(attempts: int = 3):\n    for i in range(attempts):\n        try:\n            return await obb.equity.market_snapshots(provider='intrinio')\n        except OpenBBError as e:\n            if 'Empty CSV file' in str(e) and i < attempts - 1:\n                await asyncio.sleep(2 ** i)  # signed URL expiry / transient S3 issue\n                continue\n            raise","preventionTips":["Do not cache the intermediate snapshots listing (file URLs) — always re-request it in the same run so signed URLs are fresh.","Add retry with backoff around the whole market_snapshots call for transient empty-file/CDN failures.","If a specific datetime repeatedly fails, shift the requested datetime slightly to receive a different file set.","Keep openbb-intrinio updated so fixes to non-gzip or error-body responses are picked up."],"tags":["openbb","intrinio","csv","gzip","s3-signed-url","transient"],"backgroundTag":null,"analyzedSha":"3e071fcc2cd9f891cac6040ae60296dba76dab46","analyzedAt":"2026-08-14T23:40:48.960Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}