{"record":{"id":"464f9d67856dde8a","repo":"home-assistant/core","slug":"failed-to-validate-calendarevent-err","errorCode":null,"errorMessage":"Failed to validate CalendarEvent: {err}","messagePattern":"Failed to validate CalendarEvent: (.+?)","errorType":"exception","errorClass":"HomeAssistantError","httpStatus":null,"severity":"error","filePath":"homeassistant/components/calendar/__init__.py","lineNumber":414,"sourceCode":"        return not isinstance(self.start, datetime.datetime)\n\n    def as_dict(self) -> dict[str, Any]:\n        \"\"\"Return a dict representation of the event.\"\"\"\n        return {\n            **dataclasses.asdict(self, dict_factory=_event_dict_factory),\n            \"all_day\": self.all_day,\n        }\n\n    def __post_init__(self) -> None:\n        \"\"\"Perform validation on the CalendarEvent.\"\"\"\n\n        def skip_none(obj: Iterable[tuple[str, Any]]) -> dict[str, str]:\n            return {k: v for k, v in obj if v is not None}\n\n        try:\n            CALENDAR_EVENT_SCHEMA(dataclasses.asdict(self, dict_factory=skip_none))\n        except vol.Invalid as err:\n            raise HomeAssistantError(\n                f\"Failed to validate CalendarEvent: {err}\"\n            ) from err\n\n        # It is common to set a start an end date to be the same thing for\n        # an all day event, but that is not a valid duration. Fix to have a\n        # duration of one day.\n        if (\n            not isinstance(self.start, datetime.datetime)\n            and not isinstance(self.end, datetime.datetime)\n            and self.start == self.end\n        ):\n            self.end = self.start + datetime.timedelta(days=1)\n\n\ndef _event_dict_factory(obj: Iterable[tuple[str, Any]]) -> dict[str, str]:\n    \"\"\"Convert CalendarEvent dataclass items to dictionary of attributes.\"\"\"\n    result: dict[str, str] = {}\n    for name, value in obj:","sourceCodeStart":396,"sourceCodeEnd":432,"githubUrl":"https://github.com/home-assistant/core/blob/58a3fdb3ea0538617f0a07efcfba6294de64fd59/homeassistant/components/calendar/__init__.py#L396-L432","documentation":"HomeAssistantError raised in CalendarEvent.__post_init__ when the dataclass fails CALENDAR_EVENT_SCHEMA validation (voluptuous). Any integration constructing a CalendarEvent with invalid content — bad summary type, invalid start/end types, malformed recurrence rule — trips this during object construction.","triggerScenarios":"A calendar platform integration (or a caller building CalendarEvent directly) passes fields that violate the schema: non-string summary/description, invalid datetime vs date mixing, or a bad rrule string. __post_init__ serializes the dataclass (skipping Nones) and runs it through the voluptuous schema.","commonSituations":"Custom calendar integrations mapping third-party API data with unexpected types or None handling, upstream APIs changing field formats, or local calendar YAML/webhook data inserted without sanitization.","solutions":["Inspect the values passed to CalendarEvent just before construction (log start/end/summary types)","Coerce types before construction: parse datetimes with dt_util, ensure summary/description are str or None","If writing a custom integration, validate upstream data against your own mapping before creating CalendarEvent"],"exampleFix":"# before\nevent = CalendarEvent(\n    summary=event_data[\"title\"],  # may be None or dict from a flaky API\n    start=event_data[\"start\"],  # raw string, not parsed\n    end=event_data[\"end\"],\n)\n\n# after\nfrom homeassistant.util import dt as dt_util\n\nsummary = str(event_data[\"title\"]) if event_data.get(\"title\") else \"\"\nevent = CalendarEvent(\n    summary=summary,\n    start=dt_util.parse_datetime(event_data[\"start\"]),\n    end=dt_util.parse_datetime(event_data[\"end\"]),\n)","handlingStrategy":"validation","validationCode":"from homeassistant.util import dt as dt_util\n\n# Coerce before constructing CalendarEvent\nsummary = str(raw[\"title\"]) if raw.get(\"title\") else \"\"\nstart = dt_util.parse_datetime(raw[\"start\"]) or dt_util.parse_date(raw[\"start\"])\nend = dt_util.parse_datetime(raw[\"end\"]) or dt_util.parse_date(raw[\"end\"])\nassert start is not None and end is not None","typeGuard":"from datetime import date, datetime\n\ndef is_valid_calendar_event_fields(\n    summary: object, start: object, end: object\n) -> bool:\n    return (\n        summary is None or isinstance(summary, str)\n    ) and (\n        isinstance(start, (datetime, date))\n        and isinstance(end, (datetime, date))\n        and (isinstance(start, datetime) == isinstance(end, datetime))\n    )","tryCatchPattern":"try:\n    CalendarEvent(summary=summary, start=start, end=end)\nexcept HomeAssistantError as err:\n    logger.error(\"Rejected upstream event data: %s\", err)\n    # skip or sanitize the record instead of failing the whole platform","preventionTips":["Sanitize third-party field types before constructing CalendarEvent","Parse datetimes with homeassistant.util.dt, never pass raw strings","Keep start/end in the same family (both date or both datetime)"],"tags":["homeassistant","calendar","validation","voluptuous"],"backgroundTag":null,"analyzedSha":"58a3fdb3ea0538617f0a07efcfba6294de64fd59","analyzedAt":"2026-08-14T20:54:38.818Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}