{"record":{"id":"f2bfecfc120701a9","repo":"getredash/redash","slug":"missing-columns-field-in-result-dict","errorCode":null,"errorMessage":"Missing `columns` field in `result` dict.","messagePattern":"Missing `columns` field in `result` dict\\.","errorType":"exception","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"redash/query_runner/python.py","lineNumber":325,"sourceCode":"        return user\n\n    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","sourceCodeStart":307,"sourceCodeEnd":343,"githubUrl":"https://github.com/getredash/redash/blob/ca79fe988d81cdac9675b412f3dfcab107bc1fbc/redash/query_runner/python.py#L307-L343","documentation":"Raised by PythonQueryRunner.validate_result (redash/query_runner/python.py:325) when the script's `result` dict exists and has `rows` but is missing the `columns` key. Redash requires both keys to build a result table; columns drives the table headers and column ordering in the UI.","triggerScenarios":"A Python query that assigns `result = {'rows': [...]}` without a matching `columns` list; note validation checks `rows` first, so this only fires when `rows` is present.","commonSituations":"Users assuming Redash infers columns from the row dicts (it does not); partial migrations of scripts where the columns line was dropped; copy-paste from examples that omit columns.","solutions":["Add `columns` as a list of column metadata dicts, typically `[{'name': ..., 'friendly_name': ..., 'type': ...}]`","Keep `columns` names consistent with the keys used in each row dict","Order `columns` in the display order you want in the Redash table"],"exampleFix":"# before\nresult = {'rows': [{'id': 1}]}\n# after\nresult = {'columns': [{'name': 'id', 'friendly_name': 'ID', 'type': 'integer'}], 'rows': [{'id': 1}]}","handlingStrategy":"validation","validationCode":"missing = [k for k in ('rows', 'columns') if k not in result]\nif missing:\n    raise ValueError('result dict missing: {}'.format(missing))","typeGuard":"def has_required_keys(r: dict) -> bool:\n    return isinstance(r, dict) and all(k in r for k in ('rows', 'columns'))","tryCatchPattern":"try:\n    validate_result(result)\nexcept Exception as e:\n    return None, 'Invalid result shape: {}'.format(e)","preventionTips":["Always construct result in one literal with both keys","Derive columns from the same source as rows so they cannot diverge","Add a unit check in CI for shared Python query snippets"],"tags":["python","query-runner","validation","redash"],"backgroundTag":"query-result-validation","analyzedSha":"ca79fe988d81cdac9675b412f3dfcab107bc1fbc","analyzedAt":"2026-08-28T18:32:34.637Z","schemaVersion":2},"datasetVersion":"2026-08-28T21:17:43.275Z"}