{"record":{"id":"dc4bbd095bf7a7ea","repo":"apache/superset","slug":"operation-property-of-post-processing-object-und","errorCode":null,"errorMessage":"`operation` property of post processing object undefined","messagePattern":"`operation` property of post processing object undefined","errorType":"validation","errorClass":"InvalidPostProcessingError","httpStatus":400,"severity":"error","filePath":"superset/common/query_object.py","lineNumber":544,"sourceCode":"            )\n        return cache_key\n\n    def exec_post_processing(self, df: DataFrame) -> DataFrame:\n        \"\"\"\n        Perform post processing operations on DataFrame.\n\n        :param df: DataFrame returned from database model.\n        :return: new DataFrame to which all post processing operations have been\n                 applied\n        :raises QueryObjectValidationError: If the post processing operation\n                 is incorrect\n        \"\"\"\n        logger.debug(\"post_processing: \\n %s\", pformat(self.post_processing))\n        with event_logger.log_context(f\"{self.__class__.__name__}.post_processing\"):\n            for post_process in self.post_processing:\n                operation = post_process.get(\"operation\")\n                if not operation:\n                    raise InvalidPostProcessingError(\n                        _(\"`operation` property of post processing object undefined\")\n                    )\n                if not hasattr(pandas_postprocessing, operation):\n                    raise InvalidPostProcessingError(\n                        _(\n                            \"Unsupported post processing operation: %(operation)s\",\n                            type=operation,\n                        )\n                    )\n                options = post_process.get(\"options\", {})\n                df = getattr(pandas_postprocessing, operation)(df, **options)\n            return df\n","sourceCodeStart":526,"sourceCodeEnd":557,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/common/query_object.py#L526-L557","documentation":"InvalidPostProcessingError raised in QueryObject.get_post_processing_df (query_object.py:544) when an entry in the post_processing list has no truthy operation key. Each post-processing step must be an object like {\"operation\": \"pivot\", \"options\": {...}}; without an operation name Superset cannot dispatch to the pandas_postprocessing module. This is a malformed-request error detected while applying pandas post-processing to the DataFrame returned by the database.","triggerScenarios":"Sending a chart data request whose post_processing array contains an entry missing the operation key, e.g. {\"options\": {...}} or an empty object, or where operation is null/empty string. Common when post-processing payloads are assembled dynamically or hand-written.","commonSituations":"Custom chart plugins emitting malformed post_processing; API clients copying partial JSON from docs; version upgrades where the expected post-processing schema changed and stale stored definitions omit the operation key.","solutions":["Print/inspect the post_processing array in the failing chart's formData and add the missing \"operation\" field (valid values are function names in superset/common/utils/pandas_postprocessing.py, e.g. pivot, aggregate, rolling, diff, rank, cum, contribution, proscons, boxplot).","Validate each post-processing entry client-side against a schema requiring operation:string before sending the request.","If the chart was authored in an older Superset version, re-save the chart in the current Explore UI so the stored post_processing is regenerated."],"exampleFix":"// before\n\"post_processing\": [\n  { \"options\": { \"aggregate\": { \"sum__sales\": \"sum\" } } }\n]\n\n// after\n\"post_processing\": [\n  {\n    \"operation\": \"aggregate\",\n    \"options\": { \"aggregate\": { \"sum__sales\": \"sum\" } }\n  }\n]","handlingStrategy":"validation","validationCode":"def validate_post_processing(steps: list[dict]) -> None:\n    for i, step in enumerate(steps or []):\n        if not isinstance(step, dict) or not step.get(\"operation\"):\n            raise ValueError(f\"post_processing[{i}] is missing 'operation'\")","typeGuard":"from typing import TypedDict\n\nclass PostProcessingStep(TypedDict):\n    operation: str\n    options: dict\n\ndef is_valid_post_processing_step(step: object) -> bool:\n    return (\n        isinstance(step, dict)\n        and isinstance(step.get(\"operation\"), str)\n        and bool(step[\"operation\"].strip())\n    )","tryCatchPattern":"from superset.exceptions import InvalidPostProcessingError\n\ntry:\n    df = query_object.get_post_processing_df(df)\nexcept InvalidPostProcessingError as ex:\n    logger.error(\"bad post_processing payload: %s\", query_object.post_processing)\n    raise","preventionTips":["Always emit post_processing entries as {operation, options} pairs from plugins.","Schema-validate chart data payloads (operation is a required string) before POSTing to /api/v1/chart/data.","Pin a regression test that round-trips saved chart formData through the current post-processing validator."],"tags":["post-processing","pandas","validation","superset"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}