alibaba/DataX · error · ValueError
headers for a list of dicts is not a dict or a keyword
Error message
headers for a list of dicts is not a dict or a keyword
What it means
ValueError from tabulate: rows were detected as a list of dicts, but 'headers' was given a truthy value that is neither a dict nor one of the keywords ('keys', 'firstrow'). This tabulate version only supports per-column header mapping via a dict when rows are dicts — a list of header strings is rejected.
Source
Thrown at otsstreamreader/tools/tabulate.py:747
for k in row.keys():
#Save unique items in input order
if k not in uniq_keys:
keys.append(k)
uniq_keys.add(k)
if headers == 'keys':
headers = keys
elif isinstance(headers, dict):
# a dict of headers for a list of dicts
headers = [headers.get(k, k) for k in keys]
headers = list(map(_text_type, headers))
elif headers == "firstrow":
if len(rows) > 0:
headers = [firstdict.get(k, k) for k in keys]
headers = list(map(_text_type, headers))
else:
headers = []
elif headers:
raise ValueError('headers for a list of dicts is not a dict or a keyword')
rows = [[row.get(k) for k in keys] for row in rows]
elif headers == "keys" and len(rows) > 0:
# keys are column indices
headers = list(map(_text_type, range(len(rows[0]))))
# take headers from the first row if necessary
if headers == "firstrow" and len(rows) > 0:
if index is not None:
headers = [index[0]] + list(rows[0])
index = index[1:]
else:
headers = rows[0]
headers = list(map(_text_type, headers)) # headers should be strings
rows = rows[1:]
headers = list(map(_text_type,headers))
rows = list(map(list,rows))View on GitHub (pinned to 80ec23d5c5)
Solutions
- Use headers='keys' to take column names from the dicts themselves.
- Pass headers as a dict mapping data-key -> display-name: headers={'ts': 'timestamp'}.
- Or convert rows to lists first: tabulate([[r[k] for k in keys] for r in rows], headers=key_list).
Example fix
# before
tabulate([{'ts': 1, 'v': 2}], headers=['timestamp', 'value'])
# after
tabulate([{'ts': 1, 'v': 2}], headers={'ts': 'timestamp', 'v': 'value'}) Defensive patterns
Strategy: type-guard
Validate before calling
if rows and isinstance(rows[0], dict) and not isinstance(headers, (dict, str)) and headers not in (None, False):
raise ValueError('list-of-dicts rows require headers as dict or "keys"/"firstrow"') Type guard
def headers_ok_for_dict_rows(headers):
return headers in (None, False, 'keys', 'firstrow') or isinstance(headers, dict) Prevention
- When rows are dicts, prefer headers='keys' or a rename dict.
- Convert dict rows to value lists if you must pass a header list.
When it happens
Trigger: Calling tabulate([{...}, {...}], headers=['A', 'B']) — list-of-dicts input with a list-type headers argument. Works fine with iterable-of-iterables rows, so the failure surprises users who switch row format without changing headers.
Common situations: Reusing a headers list from a list-of-lists call when the data becomes a list of dicts; dict rows produced by csv.DictReader and headers taken from reader.fieldnames.
Related errors
- index must be as long as the number of data rows
- tabular data doesn't appear to be a dict or a DataFrame
AI-assisted analysis of alibaba/DataX@80ec23d5c5 (2026-08-14).
Data as JSON: /api/errors/ab88ed94ed8c0cb2.
Report an issue: GitHub.