{"record":{"id":"d184858b30fb3810","repo":"OpenBB-finance/OpenBB","slug":"expected-dict-got-list-of-dicts","errorCode":null,"errorMessage":"Expected dict, got list of dicts","messagePattern":"Expected dict, got list of dicts","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"openbb_platform/providers/fmp/openbb_fmp/utils/helpers.py","lineNumber":138,"sourceCode":"    if isinstance(data, dict):\n        raise ValueError(\"Expected list of dicts, got dict\")\n    if len(data) == 0:\n        raise EmptyDataError()\n\n    return data\n\n\nasync def get_data_one(url: str, **kwargs: Any) -> dict:\n    \"\"\"Get data from FMP endpoint and convert to schema.\"\"\"\n    data = await get_data(url, **kwargs)\n    if isinstance(data, list):\n        if len(data) == 0:\n            raise ValueError(\"Expected dict, got empty list\")\n\n        try:\n            data = {i: data[i] for i in range(len(data))} if len(data) > 1 else data[0]\n        except TypeError as e:\n            raise ValueError(\"Expected dict, got list of dicts\") from e\n\n    return data\n\n\ndef most_recent_quarter(base: date | None = None) -> date:\n    \"\"\"Get the most recent quarter date.\"\"\"\n    if base is None:\n        base = date.today()\n    base = min(base, date.today())  # This prevents dates from being in the future\n    exacts = [(3, 31), (6, 30), (9, 30), (12, 31)]\n    for exact in exacts:\n        if base.month == exact[0] and base.day == exact[1]:\n            return base\n    if base.month < 4:\n        return date(base.year - 1, 12, 31)\n    if base.month < 7:\n        return date(base.year, 3, 31)\n    if base.month < 10:","sourceCodeStart":120,"sourceCodeEnd":156,"githubUrl":"https://github.com/OpenBB-finance/OpenBB/blob/3e071fcc2cd9f891cac6040ae60296dba76dab46/openbb_platform/providers/fmp/openbb_fmp/utils/helpers.py#L120-L156","documentation":"Raised by openbb_fmp get_data_one when the response is a list whose element access raises TypeError - in practice a list of non-subscriptable items (e.g. a list of strings or numbers) where a list of dicts was expected. For a normal multi-dict list the range-index comprehension succeeds and returns a dict of dicts, so this fires on truly malformed payloads.","triggerScenarios":"An FMP endpoint normally returning [{...}] suddenly returning ['msg1','msg2'] or [1,2,3]; passing a URL that hits an FMP error/maintenance page whose JSON body is a plain list of strings.","commonSituations":"FMP API changes or intermittent maintenance payloads; pointing base_url at the wrong API version so the endpoint resolves to a different resource; provider/openbb version skew after FMP updates.","solutions":["Curl the URL with the same key and inspect the exact JSON type of the list elements","Update openbb-fmp to the latest version to pick up endpoint fixes","Verify the URL template matches the current FMP API docs (v3 vs v4 paths)","Treat as transient if FMP status page shows degradation and retry later"],"exampleFix":null,"handlingStrategy":"type-guard","validationCode":"raw = await get_data(url)\nif isinstance(raw, list) and raw and not isinstance(raw[0], dict):\n    raise RuntimeError(f'FMP returned malformed list payload: {raw!r:.200}')","typeGuard":"def is_list_of_dicts(data: object) -> bool:\n    return isinstance(data, list) and bool(data) and all(isinstance(i, dict) for i in data)","tryCatchPattern":"try:\n    data = await get_data_one(url)\nexcept ValueError as e:\n    if 'list of dicts' in str(e):\n        log.error('FMP response shape changed for %s', url)\n    raise","preventionTips":["Type-check list element shapes before indexing when consuming FMP directly","Keep the FMP provider package updated to track API changes","Alert on this error - it indicates an API contract break, not bad input"],"tags":["fmp","type-mismatch","malformed-response"],"backgroundTag":null,"analyzedSha":"3e071fcc2cd9f891cac6040ae60296dba76dab46","analyzedAt":"2026-08-14T23:40:48.960Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}