{"record":{"id":"b66ecb5a52164bea","repo":"phacility/phabricator","slug":"unable-to-write-ics-document-event-has-no-modifie","errorCode":null,"errorMessage":"Unable to write ICS document: event has no modified time, but each event MUST have a modified time.","messagePattern":"Unable to write ICS document: event has no modified time, but each event MUST have a modified time\\.","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/calendar/parser/ics/PhutilICSWriter.php","lineNumber":163,"sourceCode":"      throw new Exception(\n        pht(\n          'Unable to write ICS document: event has no UID, but each event '.\n          'MUST have a UID.'));\n    }\n    $properties[] = $this->newTextProperty(\n      'UID',\n      $uid);\n\n    $created = $event->getCreatedDateTime();\n    if ($created) {\n      $properties[] = $this->newDateTimeProperty(\n        'CREATED',\n        $event->getCreatedDateTime());\n    }\n\n    $dtstamp = $event->getModifiedDateTime();\n    if (!$dtstamp) {\n      throw new Exception(\n        pht(\n          'Unable to write ICS document: event has no modified time, but '.\n          'each event MUST have a modified time.'));\n    }\n    $properties[] = $this->newDateTimeProperty(\n      'DTSTAMP',\n      $dtstamp);\n\n    $dtstart = $event->getStartDateTime();\n    if ($dtstart) {\n      $properties[] = $this->newDateTimeProperty(\n        'DTSTART',\n        $dtstart);\n    }\n\n    $dtend = $event->getEndDateTime();\n    if ($dtend) {\n      $properties[] = $this->newDateTimeProperty(","sourceCodeStart":145,"sourceCodeEnd":181,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/calendar/parser/ics/PhutilICSWriter.php#L145-L181","documentation":"While writing an ICS document, PhutilICSWriter requires every event to have a modified datetime because it must emit a DTSTAMP property (RFC5545 marks DTSTAMP as REQUIRED on VEVENT). If getModifiedDateTime() returns null the writer aborts rather than produce a document no client will accept.","triggerScenarios":"Writing an event node whose setModifiedDateTime() was never called, or whose imported source dictionary lacks the modified/stamp field — commonly right after error 228 was fixed and UID got set but the timestamp was overlooked.","commonSituations":"Import pipelines that fill created/start/end but not modified; exporting test fixture events; migrating events from legacy storage where the modified column is NULL.","solutions":["Set a modified datetime before export: $event->setModifiedDateTime(PhutilCalendarAbsoluteDateTime::newFromEpoch(time()))","If the source has a real modification date, map it into the node instead of defaulting silently","Centralize the UID+DTSTAMP checks in one pre-export validator"],"exampleFix":"// before\n$ics = PhutilICSWriter::writeDocuments(array($event));\n\n// after\nif (!$event->getModifiedDateTime()) {\n  $event->setModifiedDateTime(\n    PhutilCalendarAbsoluteDateTime::newFromEpoch(time()));\n}\n$ics = PhutilICSWriter::writeDocuments(array($event));","handlingStrategy":"validation","validationCode":"foreach ($events as $event) {\n  if (!$event->getModifiedDateTime()) {\n    $event->setModifiedDateTime(\n      PhutilCalendarAbsoluteDateTime::newFromEpoch(time()));\n  }\n}\n$ics = PhutilICSWriter::writeDocuments($events);","typeGuard":null,"tryCatchPattern":"try {\n  $ics = PhutilICSWriter::writeDocuments(array($event));\n} catch (Exception $ex) {\n  if (strpos($ex->getMessage(), 'modified time') !== false) {\n    $event->setModifiedDateTime(\n      PhutilCalendarAbsoluteDateTime::newFromEpoch(time()));\n    $ics = PhutilICSWriter::writeDocuments(array($event));\n  } else {\n    throw $ex;\n  }\n}","preventionTips":["Treat UID + DTSTAMP (modified time) as one required pair in export helpers","Default the modified time to the ingestion timestamp when the source has none","Write fixtures for events missing optional fields to keep export code honest"],"tags":["ics","icalendar","rfc5545","calendar","export","dtstamp"],"backgroundTag":"missing-required-field","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}