{"record":{"id":"39139d10e804bb07","repo":"pola-rs/polars","slug":"expected-list-or-dict-of-objects","errorCode":null,"errorMessage":"expected list or dict of objects","messagePattern":"expected list or dict of objects","errorType":"exception","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"py-polars/src/polars/convert/normalize.py","lineNumber":246,"sourceCode":"    ╞══════╪════════════╪═══════════════════════════════╡\n    │ 1    ┆ Cole Volk  ┆ b\"{\"height\":180,\"weight\":85}\" │\n    │ 2    ┆ Faye Raker ┆ b\"{\"height\":155,\"weight\":58}\" │\n    │ null ┆ Mark Reg   ┆ b\"{\"height\":170,\"weight\":78}\" │\n    └──────┴────────────┴───────────────────────────────┘\n    \"\"\"\n    if max_level is None:\n        max_level = 1 << 32  # eg: u32\n    max_level += 1\n\n    if isinstance(data, Sequence) and len(data) == 0:\n        return DataFrame(schema=schema)\n    elif isinstance(data, Mapping):\n        data = [data]\n    elif isinstance(data, Iterable) and not isinstance(data, str):  # type: ignore[redundant-expr]\n        data = list(data)\n    else:\n        msg = \"expected list or dict of objects\"\n        raise ValueError(msg)\n\n    if encoder is None:\n        encoder = json.dumps\n\n    return DataFrame(\n        _simple_json_normalize(\n            data,\n            separator=separator,\n            max_level=max_level,\n            encoder=encoder,\n        ),\n        schema=schema,\n        strict=strict,\n        infer_schema_length=infer_schema_length,\n    )\n","sourceCodeStart":228,"sourceCodeEnd":262,"githubUrl":"https://github.com/pola-rs/polars/blob/df599052daf96e7a9cc30a3b0c6bd25d6947e3c0/py-polars/src/polars/convert/normalize.py#L228-L262","documentation":"Raised by polars.json_normalize when `data` is not a Mapping, not a non-str Iterable/Sequence, or is a bare string. The function flattens semi-structured JSON objects, so it accepts a dict (single record), a list of dicts, or any non-string iterable of records; scalars, strings, None, and bytes all fall to the else branch and raise.","triggerScenarios":"pl.json_normalize('{\"a\": 1}') with a raw JSON string instead of parsed objects; pl.json_normalize(5), pl.json_normalize(None), or pl.json_normalize(b'...'); passing a single non-dict scalar or a str column value.","commonSituations":"Reading an API response body and forgetting json.loads / response.json(); handling a JSON column where one row is a plain string; feeding the whole text of a JSONL file rather than iterating parsed lines.","solutions":["Parse the text first: pl.json_normalize(json.loads(text)) or json_normalize(response.json())","For a single object, pass the dict itself or wrap it in a list","For JSONL/ndjson, parse per line: pl.json_normalize([json.loads(line) for line in text.splitlines()])","For whole-file JSON, prefer pl.read_json(path) which handles structure directly"],"exampleFix":"# before\npl.json_normalize('{\"a\": 1, \"b\": {\"c\": 2}}')\n\n# after\nimport json\npl.json_normalize(json.loads('{\"a\": 1, \"b\": {\"c\": 2}}'))","handlingStrategy":"type-guard","validationCode":"from collections.abc import Mapping\n\ndef normalize_input(data):\n    import json\n    if isinstance(data, str):\n        data = json.loads(data)\n    if isinstance(data, Mapping):\n        data = [data]\n    return list(data)  # ready for pl.json_normalize","typeGuard":"from collections.abc import Mapping, Iterable\nfrom typing import TypeGuard\n\ndef is_json_normalizable(data: object) -> TypeGuard[Mapping | list[Mapping]]:\n    return isinstance(data, Mapping) or (\n        isinstance(data, Iterable) and not isinstance(data, (str, bytes))\n    )","tryCatchPattern":null,"preventionTips":["Always json.loads() response bodies before json_normalize","Wrap single JSON objects in a list for uniform handling","For whole files use pl.read_json rather than manual normalize"],"tags":["json","json-normalize","type-mismatch","parsing"],"backgroundTag":null,"analyzedSha":"df599052daf96e7a9cc30a3b0c6bd25d6947e3c0","analyzedAt":"2026-08-16T12:10:03.978Z","schemaVersion":2},"datasetVersion":"2026-08-16T13:17:31.715Z"}