{"record":{"id":"ec450c24d2fbb440","repo":"getredash/redash","slug":"columns-field-should-be-of-type-list","errorCode":null,"errorMessage":"`columns` field should be of type `list`.","messagePattern":"`columns` field should be of type `list`\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"redash/query_runner/python.py","lineNumber":329,"sourceCode":"\n    def validate_result(self, result):\n        \"\"\"Validate the result after executing the query.\n\n        Parameters:\n        :result dict: The result dict.\n        \"\"\"\n        if not result:\n            raise Exception(\"local variable `result` should not be empty.\")\n        if not isinstance(result, dict):\n            raise Exception(\"local variable `result` should be of type `dict`.\")\n        if \"rows\" not in result:\n            raise Exception(\"Missing `rows` field in `result` dict.\")\n        if \"columns\" not in result:\n            raise Exception(\"Missing `columns` field in `result` dict.\")\n        if not isinstance(result[\"rows\"], list):\n            raise Exception(\"`rows` field should be of type `list`.\")\n        if not isinstance(result[\"columns\"], list):\n            raise Exception(\"`columns` field should be of type `list`.\")\n\n    def run_query(self, query, user):\n        self._current_user = user\n\n        try:\n            error = None\n\n            code = compile_restricted(query, \"<string>\", \"exec\")\n\n            builtins = safe_builtins.copy()\n            builtins[\"_write_\"] = self.custom_write\n            builtins[\"__import__\"] = self.custom_import\n            builtins[\"_getattr_\"] = safer_getattr\n            builtins[\"getattr\"] = safer_getattr\n            builtins[\"_setattr_\"] = guarded_setattr\n            builtins[\"setattr\"] = guarded_setattr\n            builtins[\"_getitem_\"] = self.custom_get_item\n            builtins[\"_getiter_\"] = self.custom_get_iter","sourceCodeStart":311,"sourceCodeEnd":347,"githubUrl":"https://github.com/getredash/redash/blob/ca79fe988d81cdac9675b412f3dfcab107bc1fbc/redash/query_runner/python.py#L311-L347","documentation":"Raised by PythonQueryRunner.validate_result (redash/query_runner/python.py:329) when `result['columns']` exists but is not a list. Columns must be a Python list of column definition objects (dicts with at least `name`), so strings, tuples, pandas Index objects, or generators fail the isinstance check.","triggerScenarios":"Assigning `result['columns'] = df.columns` (a pandas Index), a comma-separated string like `\"id,name\"`, a tuple, or a dict of column types.","commonSituations":"Pandas users passing `df.columns` directly instead of converting it; users building a string of headers; JSON-encoded column definitions that were never parsed.","solutions":["Convert pandas Index to a list of dicts: `result['columns'] = [{'name': c, 'friendly_name': c, 'type': str(df[c].dtype)} for c in df.columns]`","If columns arrive as JSON, parse with `json.loads` before assigning","Ensure it is a plain Python list, not a tuple or string"],"exampleFix":"# before\nresult = {'columns': df.columns, 'rows': df.to_dict('records')}\n# after\nresult = {'columns': [{'name': c, 'friendly_name': c} for c in df.columns], 'rows': df.to_dict('records')}","handlingStrategy":"type-guard","validationCode":"cols = result.get('columns')\nif not isinstance(cols, list):\n    result['columns'] = [{'name': c} if isinstance(c, str) else c for c in cols]","typeGuard":"def columns_is_list(r: dict) -> bool:\n    return isinstance(r.get('columns'), list)","tryCatchPattern":"try:\n    validate_result(result)\nexcept Exception as e:\n    raise Exception('columns must be a list of dicts; got {}: {}'.format(type(result.get('columns')).__name__, e))","preventionTips":["Use list(df.columns) conversions for pandas, never df.columns directly","Build columns with a helper returning [{'name': n, 'friendly_name': n} for n in names]","Validate result shape in a shared test helper"],"tags":["python","query-runner","type-validation","redash"],"backgroundTag":"query-result-validation","analyzedSha":"ca79fe988d81cdac9675b412f3dfcab107bc1fbc","analyzedAt":"2026-08-28T18:32:34.637Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}