{"record":{"id":"7a0cbdde909c7354","repo":"OpenBB-finance/OpenBB","slug":"at-least-one-extension-type-must-be-selected","errorCode":null,"errorMessage":"At least one extension type must be selected.","messagePattern":"At least one extension type must be selected\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"cookiecutter/openbb_cookiecutter/cli.py","lineNumber":32,"sourceCode":"    \"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)\n    default_pkg = context[\"project_name\"].lower().replace(\" \", \"_\").replace(\"-\", \"_\")\n    context[\"package_name\"] = Prompt.ask(\"  package_name\", default=default_pkg)\n\n    if preset_extension_types:\n        types = preset_extension_types","sourceCodeStart":14,"sourceCodeEnd":50,"githubUrl":"https://github.com/OpenBB-finance/OpenBB/blob/3e071fcc2cd9f891cac6040ae60296dba76dab46/cookiecutter/openbb_cookiecutter/cli.py#L14-L50","documentation":"Raised by the CPI query-params field validator when a country token (after spaces become underscores and the string is split on commas) is neither an ISO3 code in `CPI_CODE_SET` nor a snake_case name in `CPI_LABEL_TO_CODE`. The CPI dataset maintains its own country mapping, which is narrower than the full IMF country list, so codes valid elsewhere can still fail here. It is a pydantic `field_validator` on `mode='before'`, so it rejects the model at construction time.","triggerScenarios":"Passing a country like `country='ZWE'` when ZWE is not in the CPI code set, or `country='cote_divoire'` when the mapping spells it differently, or a comma list containing one bad token (`country='USA,EUROZONE'` where EUROZONE is an area, not a CPI country). Input is uppercased for code lookup and lowercased for name lookup, so case itself is never the issue.","commonSituations":"Reusing country lists built for other IMF endpoints (DOT, IFS) that accept a wider set; IMF-specific spellings (e.g. 'cote_d_ivoire', 'sao_tome_and_principe') not matching a caller's 'ivory_coast' style; new/renamed countries not present in the provider's pinned CPI mapping (version drift).","solutions":["Use exact ISO3 codes from the provider's CPI mapping: import `CPI_CODE_SET` from `openbb_imf.models.consumer_price_index` and pick codes from it.","For names, use the snake_case form the mapping expects (check `CPI_LABEL_TO_CODE` keys, e.g. 'united_states').","Split your list and validate each token before the call so one bad country doesn't reject the whole request.","Upgrade the openbb-imf package if the country was added/renamed upstream."],"exampleFix":"# before\nres = obb.economy.cpi(provider='imf', country='EUROZONE', expenditure='all')\n\n# after\nres = obb.economy.cpi(provider='imf', country='USA', expenditure='all')","handlingStrategy":"validation","validationCode":"from openbb_imf.models.consumer_price_index import CPI_CODE_SET, CPI_LABEL_TO_CODE\n\ndef normalize_cpi_country(raw: str) -> str:\n    tokens = raw.replace(' ', '_').split(',')\n    out = []\n    for t in tokens:\n        u, l = t.upper(), t.lower()\n        if u in CPI_CODE_SET:\n            out.append(u)\n        elif l in CPI_LABEL_TO_CODE:\n            out.append(CPI_LABEL_TO_CODE[l])\n        else:\n            raise ValueError(f'Country {t!r} not in the IMF CPI mapping; pick from CPI_CODE_SET.')\n    return ','.join(out)\n\ncountry = normalize_cpi_country('united_states,deu')","typeGuard":"from openbb_imf.models.consumer_price_index import CPI_CODE_SET, CPI_LABEL_TO_CODE\n\ndef is_valid_cpi_country(raw: str) -> bool:\n    t = raw.replace(' ', '_')\n    return t.upper() in CPI_CODE_SET or t.lower() in CPI_LABEL_TO_CODE","tryCatchPattern":null,"preventionTips":["Prefer ISO3 codes over names — fewer spelling pitfalls.","Validate multi-country lists token by token so one bad entry is identified, not the whole list rejected.","Keep the provider package updated so the CPI mapping tracks IMF country changes."],"tags":["imf","openbb","cpi","validation","country-code","pydantic"],"backgroundTag":null,"analyzedSha":"3e071fcc2cd9f891cac6040ae60296dba76dab46","analyzedAt":"2026-08-14T23:40:48.960Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}