{"record":{"id":"a459438ca9e53088","repo":"apache/superset","slug":"python-date-format-is-an-invalid-date-timestamp-fo","errorCode":null,"errorMessage":"python_date_format is an invalid date/timestamp format.","messagePattern":"python_date_format is an invalid date/timestamp format\\.","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"superset/daos/dataset.py","lineNumber":447,"sourceCode":"\n            if \"metrics\" in attributes:\n                cls.update_metrics(item, attributes.pop(\"metrics\"))\n                force_update = True\n\n            if force_update:\n                attributes[\"changed_on\"] = datetime.now()\n\n        return super().update(item, attributes)\n\n    @classmethod\n    def _validate_column_date_formats(\n        cls, property_columns: list[dict[str, Any]]\n    ) -> None:\n        for column in property_columns:\n            if column.get(\"python_date_format\") is None:\n                continue\n            if not DatasetDAO.validate_python_date_format(column[\"python_date_format\"]):\n                raise ValueError(\n                    \"python_date_format is an invalid date/timestamp format.\"\n                )\n\n    @classmethod\n    def _override_columns(\n        cls, model: SqlaTable, property_columns: list[dict[str, Any]]\n    ) -> None:\n        \"\"\"Replace columns by natural key (``column_name``) — update in place\n        rather than delete-and-reinsert.\n\n        SPIKE (full-Continuum): the previous\n        delete-and-reinsert pattern produced overlapping shadow rows in\n        ``table_columns_version`` (the same ``column_name`` had a DELETE\n        shadow at tx N alongside an INSERT shadow at tx N for a fresh PK).\n        Continuum's ``Reverter`` couldn't unwind this on restore: its flush\n        ordering inserts the historical row before deleting the live one,\n        hitting the ``UNIQUE (table_id, column_name)`` constraint mid-flush\n        (ADR-004 Failure 1).","sourceCodeStart":429,"sourceCodeEnd":465,"githubUrl":"https://github.com/apache/superset/blob/f4587218dd19d046c3e4d00063e7d27f8a2ed354/superset/daos/dataset.py#L429-L465","documentation":"ValueError raised by DatasetDAO._validate_column_date_formats while updating a dataset: a column in the payload carries a non-None `python_date_format` that fails DatasetDAO.validate_python_date_format (a datetime.strptime round-trip check). It fires before super().update() persists anything.","triggerScenarios":"PUT/PATCH /api/v1/dataset/<id> (or column-edit flows) with columns[] entries whose python_date_format is not a valid Python strptime pattern — e.g. 'YYYY-MM-DD' (moment/ISO-style) instead of '%Y-%m-%d', or a typo like '%Y-%m-%'.","commonSituations":"Confusing Superset's two format dialects: python_date_format expects strptime codes while `db_engine_spec`-side/Java or moment-style tokens ('yyyy-MM-dd') belong elsewhere; integrations writing column payloads generated from JSON Schema examples with moment tokens.","solutions":["Convert the format to Python strptime syntax: '%Y-%m-%d %H:%M:%S' etc. (moment-style 'YYYY-MM-DD' is the most common mistake).","Leave python_date_format null to let Superset infer temporal formatting.","Validate candidate formats client-side with datetime.strptime(now, fmt) before submitting."],"exampleFix":"# before\ncolumns=[{\"column_name\": \"dt\", \"python_date_format\": \"YYYY-MM-DD\"}]\n\n# after\ncolumns=[{\"column_name\": \"dt\", \"python_date_format\": \"%Y-%m-%d\"}]","handlingStrategy":"validation","validationCode":"from datetime import datetime\n\ndef valid_python_date_format(fmt: str | None) -> bool:\n    if fmt is None:\n        return True\n    try:\n        datetime.now().strftime(fmt)  # round-trip sanity\n        datetime.strptime(datetime.now().strftime(fmt), fmt)\n        return True\n    except (ValueError, TypeError):\n        return False","typeGuard":"import re\n\ndef is_strptime_format(fmt: str) -> bool:\n    # reject common moment/Java-style tokens\n    return not re.search(r\"(?<!%)YYYY|(?<!%)MM|(?<!%)DD|yyyy|dd\", fmt)","tryCatchPattern":"try:\n    DatasetDAO.update(dataset, {\"columns\": columns})\nexcept ValueError as ex:\n    if 'python_date_format' in str(ex):\n        # fix the offending format tokens and resubmit once\n        ...","preventionTips":["python_date_format uses strptime codes (%Y %m %d), never moment tokens (YYYY-MM-DD).","Null the field to use Superset's automatic temporal inference.","Run a strptime round-trip test on generated format strings in dataset-sync tooling."],"tags":["dao","dataset","date-format","validation","strptime"],"backgroundTag":null,"analyzedSha":"f4587218dd19d046c3e4d00063e7d27f8a2ed354","analyzedAt":"2026-08-14T22:39:27.425Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}