pola-rs/polars · error · ValueError

expected list or dict of objects

Error message

expected list or dict of objects

What it means

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.

Source

Thrown at py-polars/src/polars/convert/normalize.py:246

    ╞══════╪════════════╪═══════════════════════════════╡
    │ 1    ┆ Cole Volk  ┆ b"{"height":180,"weight":85}" │
    │ 2    ┆ Faye Raker ┆ b"{"height":155,"weight":58}" │
    │ null ┆ Mark Reg   ┆ b"{"height":170,"weight":78}" │
    └──────┴────────────┴───────────────────────────────┘
    """
    if max_level is None:
        max_level = 1 << 32  # eg: u32
    max_level += 1

    if isinstance(data, Sequence) and len(data) == 0:
        return DataFrame(schema=schema)
    elif isinstance(data, Mapping):
        data = [data]
    elif isinstance(data, Iterable) and not isinstance(data, str):  # type: ignore[redundant-expr]
        data = list(data)
    else:
        msg = "expected list or dict of objects"
        raise ValueError(msg)

    if encoder is None:
        encoder = json.dumps

    return DataFrame(
        _simple_json_normalize(
            data,
            separator=separator,
            max_level=max_level,
            encoder=encoder,
        ),
        schema=schema,
        strict=strict,
        infer_schema_length=infer_schema_length,
    )

View on GitHub (pinned to df599052da)

Solutions

  1. Parse the text first: pl.json_normalize(json.loads(text)) or json_normalize(response.json())
  2. For a single object, pass the dict itself or wrap it in a list
  3. For JSONL/ndjson, parse per line: pl.json_normalize([json.loads(line) for line in text.splitlines()])
  4. For whole-file JSON, prefer pl.read_json(path) which handles structure directly

Example fix

# before
pl.json_normalize('{"a": 1, "b": {"c": 2}}')

# after
import json
pl.json_normalize(json.loads('{"a": 1, "b": {"c": 2}}'))
Defensive patterns

Strategy: type-guard

Validate before calling

from collections.abc import Mapping

def normalize_input(data):
    import json
    if isinstance(data, str):
        data = json.loads(data)
    if isinstance(data, Mapping):
        data = [data]
    return list(data)  # ready for pl.json_normalize

Type guard

from collections.abc import Mapping, Iterable
from typing import TypeGuard

def is_json_normalizable(data: object) -> TypeGuard[Mapping | list[Mapping]]:
    return isinstance(data, Mapping) or (
        isinstance(data, Iterable) and not isinstance(data, (str, bytes))
    )

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Related errors


AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16). Data as JSON: /api/errors/39139d10e804bb07. Report an issue: GitHub.