{"record":{"id":"84203fb657739269","repo":"getredash/redash","slug":"rows-field-should-be-of-type-list","errorCode":null,"errorMessage":"`rows` field should be of type `list`.","messagePattern":"`rows` field should be of type `list`\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"redash/query_runner/python.py","lineNumber":327,"sourceCode":"    def test_connection(self):\n        pass\n\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","sourceCodeStart":309,"sourceCodeEnd":345,"githubUrl":"https://github.com/getredash/redash/blob/ca79fe988d81cdac9675b412f3dfcab107bc1fbc/redash/query_runner/python.py#L309-L345","documentation":"Raised by PythonQueryRunner.validate_result (redash/query_runner/python.py:327) when `result['rows']` is present but is not a Python list. The rows contract is strictly a list (usually of dicts keyed by column name), so tuples, generators, DataFrames, dicts, or JSON strings all fail this isinstance check.","triggerScenarios":"Assigning `result['rows'] = df` (a pandas DataFrame), `result['rows'] = tuple(...)`, a generator expression, a numpy array, or a JSON-encoded string instead of an actual list.","commonSituations":"Pandas users returning the DataFrame directly; users returning `df.to_json()` or `df.values`; converting via `df.itertuples()` which yields a generator.","solutions":["Convert to a list of dicts: `result['rows'] = df.to_dict(orient='records')` or `[dict(r) for r in rows]`","If rows are JSON strings, parse first: `json.loads(payload)`","Ensure each element is a dict whose keys match the `columns` names"],"exampleFix":"# before\nresult = {'columns': cols, 'rows': df}\n# after\nresult = {'columns': cols, 'rows': df.to_dict(orient='records')}","handlingStrategy":"type-guard","validationCode":"rows = result.get('rows')\nif not isinstance(rows, list):\n    result['rows'] = list(rows) if hasattr(rows, '__iter__') and not isinstance(rows, dict) else []","typeGuard":"def rows_is_list(r: dict) -> bool:\n    return isinstance(r.get('rows'), list)","tryCatchPattern":"try:\n    validate_result(result)\nexcept Exception as e:\n    raise Exception('rows must be a list of dicts; got {}: {}'.format(type(result.get('rows')).__name__, e))","preventionTips":["Never assign a DataFrame, tuple, or generator to result['rows']","Convert once at the end: result['rows'] = list(map(dict, rows))","JSON-serialize a sample of rows in dev to catch non-serializable values early"],"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"}