{"record":{"id":"ab88ed94ed8c0cb2","repo":"alibaba/DataX","slug":"headers-for-a-list-of-dicts-is-not-a-dict-or-a-key","errorCode":null,"errorMessage":"headers for a list of dicts is not a dict or a keyword","messagePattern":"headers for a list of dicts is not a dict or a keyword","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"otsstreamreader/tools/tabulate.py","lineNumber":747,"sourceCode":"                for k in row.keys():\n                    #Save unique items in input order\n                    if k not in uniq_keys:\n                        keys.append(k)\n                        uniq_keys.add(k)\n            if headers == 'keys':\n                headers = keys\n            elif isinstance(headers, dict):\n                # a dict of headers for a list of dicts\n                headers = [headers.get(k, k) for k in keys]\n                headers = list(map(_text_type, headers))\n            elif headers == \"firstrow\":\n                if len(rows) > 0:\n                    headers = [firstdict.get(k, k) for k in keys]\n                    headers = list(map(_text_type, headers))\n                else:\n                    headers = []\n            elif headers:\n                raise ValueError('headers for a list of dicts is not a dict or a keyword')\n            rows = [[row.get(k) for k in keys] for row in rows]\n\n        elif headers == \"keys\" and len(rows) > 0:\n            # keys are column indices\n            headers = list(map(_text_type, range(len(rows[0]))))\n\n    # take headers from the first row if necessary\n    if headers == \"firstrow\" and len(rows) > 0:\n        if index is not None:\n            headers = [index[0]] + list(rows[0])\n            index = index[1:]\n        else:\n            headers = rows[0]\n        headers = list(map(_text_type, headers)) # headers should be strings\n        rows = rows[1:]\n\n    headers = list(map(_text_type,headers))\n    rows = list(map(list,rows))","sourceCodeStart":729,"sourceCodeEnd":765,"githubUrl":"https://github.com/alibaba/DataX/blob/80ec23d5c5328eb90ca364d2749e92dfaf44541e/otsstreamreader/tools/tabulate.py#L729-L765","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"# before\ntabulate([{'ts': 1, 'v': 2}], headers=['timestamp', 'value'])\n# after\ntabulate([{'ts': 1, 'v': 2}], headers={'ts': 'timestamp', 'v': 'value'})","handlingStrategy":"type-guard","validationCode":"if rows and isinstance(rows[0], dict) and not isinstance(headers, (dict, str)) and headers not in (None, False):\n    raise ValueError('list-of-dicts rows require headers as dict or \"keys\"/\"firstrow\"')","typeGuard":"def headers_ok_for_dict_rows(headers):\n    return headers in (None, False, 'keys', 'firstrow') or isinstance(headers, dict)","tryCatchPattern":null,"preventionTips":["When rows are dicts, prefer headers='keys' or a rename dict.","Convert dict rows to value lists if you must pass a header list."],"tags":["python","tabulate","headers","dev-tools"],"backgroundTag":null,"analyzedSha":"80ec23d5c5328eb90ca364d2749e92dfaf44541e","analyzedAt":"2026-08-14T15:33:51.187Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}