alibaba/DataX · error · ValueError

tabular data doesn't appear to be a dict or a DataFrame

Error message

tabular data doesn't appear to be a dict or a DataFrame

What it means

ValueError from tabulate._format: the input has a keys()/values() shape (treated as dict-like) but neither callable .values() nor a usable .index attribute, so this old (0.7.x-era) tabulate cannot recognize it as a dict or a pandas DataFrame. It is a duck-typing heuristic failure against newer/custom mapping or DataFrame-like types.

Source

Thrown at otsstreamreader/tools/tabulate.py:699

        is_headers2bool_broken = True
        headers = list(headers)

    index = None
    if hasattr(tabular_data, "keys") and hasattr(tabular_data, "values"):
        # dict-like and pandas.DataFrame?
        if hasattr(tabular_data.values, "__call__"):
            # likely a conventional dict
            keys = tabular_data.keys()
            rows = list(izip_longest(*tabular_data.values()))  # columns have to be transposed
        elif hasattr(tabular_data, "index"):
            # values is a property, has .index => it's likely a pandas.DataFrame (pandas 0.11.0)
            keys = tabular_data.keys()
            vals = tabular_data.values  # values matrix doesn't need to be transposed
            # for DataFrames add an index per default
            index = list(tabular_data.index)
            rows = [list(row) for row in vals]
        else:
            raise ValueError("tabular data doesn't appear to be a dict or a DataFrame")

        if headers == "keys":
            headers = list(map(_text_type,keys))  # headers should be strings

    else:  # it's a usual an iterable of iterables, or a NumPy array
        rows = list(tabular_data)

        if (headers == "keys" and
            hasattr(tabular_data, "dtype") and
            getattr(tabular_data.dtype, "names")):
            # numpy record array
            headers = tabular_data.dtype.names
        elif (headers == "keys"
              and len(rows) > 0
              and isinstance(rows[0], tuple)
              and hasattr(rows[0], "_fields")):
            # namedtuple
            headers = list(map(_text_type, rows[0]._fields))

View on GitHub (pinned to 80ec23d5c5)

Solutions

  1. Convert before calling: pass list(df.itertuples(index=False)) or [list(r) for r in df.values].
  2. Pass a plain dict {col: list_of_values} which the heuristic supports.
  3. Upgrade/replace the vendored tabulate with a current release that handles modern pandas.

Example fix

# before
tabulate(df, headers='keys')
# after
tabulate([list(r) for r in df.itertuples(index=False)], headers=list(df.columns))
Defensive patterns

Strategy: type-guard

Validate before calling

if hasattr(tabular_data, 'keys') and not isinstance(tabular_data, dict):
    tabular_data = list(tabular_data.values())  # or convert DataFrame rows explicitly

Type guard

def is_supported_mapping(o):
    return isinstance(o, dict) or (hasattr(o, 'values') and callable(o.values)) or hasattr(o, 'index')

Prevention

When it happens

Trigger: Passing a mapping-like object (has keys but .values is a property, e.g. modern pandas DataFrame normally has .index so fails earlier heuristics; or OrderedDict subclasses, or dict-like ORM results) that matches neither the 0.11-era DataFrame fingerprint nor a plain dict.

Common situations: Vendored old tabulate used with modern pandas/numpy versions whose APIs moved; passing dict_items, generators, or custom Mapping objects to the otsstreamreader tooling.

Related errors


AI-assisted analysis of alibaba/DataX@80ec23d5c5 (2026-08-14). Data as JSON: /api/errors/9bb51af5eaf384b6. Report an issue: GitHub.