{"record":{"id":"8db6dfe9cf2aca90","repo":"phacility/phabricator","slug":"unable-to-write-ics-document-event-has-no-uid-bu","errorCode":null,"errorMessage":"Unable to write ICS document: event has no UID, but each event MUST have a UID.","messagePattern":"Unable to write ICS document: event has no UID, but each event MUST have a UID\\.","errorType":"validation","errorClass":"Exception","httpStatus":null,"severity":"error","filePath":"src/applications/calendar/parser/ics/PhutilICSWriter.php","lineNumber":145,"sourceCode":"      '2.0');\n\n    $properties[] = $this->newTextProperty(\n      'PRODID',\n      self::getICSPRODID());\n\n    return $properties;\n  }\n\n  public static function getICSPRODID() {\n    return '-//Phacility//Phabricator//EN';\n  }\n\n  private function getEventNodeProperties(PhutilCalendarEventNode $event) {\n    $properties = array();\n\n    $uid = $event->getUID();\n    if (!strlen($uid)) {\n      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(","sourceCodeStart":127,"sourceCodeEnd":163,"githubUrl":"https://github.com/phacility/phabricator/blob/5720a38cfe95b00ca4be5016dd0d2f3195f4fa04/src/applications/calendar/parser/ics/PhutilICSWriter.php#L127-L163","documentation":"PhutilICSWriter refuses to serialize a PhutilCalendarEventNode that has an empty UID. RFC5545 requires a UID property on every VEVENT (it is the identity used by clients to correlate events across updates), so the writer fails fast rather than emitting an invalid ICS document.","triggerScenarios":"Calling PhutilICSWriter::writeDocuments(array($event)) on an event node built with newFromDictionary() or by hand where setUID() was never called, or where the source data (e.g. a foreign calendar row) has a null/empty UID column.","commonSituations":"Exporting events ingested from systems that do not guarantee UIDs; custom event-node builders in tests or migration scripts that skip identity fields; exporting freshly constructed stub events.","solutions":["Always set a UID before export: $event->setUID(...)","Generate one when the source lacks it, e.g. Filesystem::readRandomCharacters(12) or a PHID-derived value","Validate the whole node (UID present) in a shared export helper so every code path is covered"],"exampleFix":"// before\n$event = PhutilCalendarEventNode::newFromDictionary($dict);\n$ics = PhutilICSWriter::writeDocuments(array($event));\n\n// after\n$event = PhutilCalendarEventNode::newFromDictionary($dict);\nif (!strlen($event->getUID())) {\n  $event->setUID(Filesystem::readRandomCharacters(12));\n}\n$ics = PhutilICSWriter::writeDocuments(array($event));","handlingStrategy":"validation","validationCode":"foreach ($events as $event) {\n  if (!strlen($event->getUID())) {\n    throw new Exception(\n      pht('Refusing to export event without a UID: %s', $event->getName()));\n  }\n}\n$ics = PhutilICSWriter::writeDocuments($events);","typeGuard":null,"tryCatchPattern":"try {\n  $ics = PhutilICSWriter::writeDocuments(array($event));\n} catch (Exception $ex) {\n  // Enrich the failure with which event lacked the UID before rethrowing.\n  throw new Exception('Event '.$event->getPHID().' failed ICS export: '.$ex->getMessage());\n}","preventionTips":["Make UID assignment part of the canonical event-node builder, not each export site","Generate and persist a UID at ingestion time for foreign events lacking one","Add a pre-export validator that checks UID and modified-time together"],"tags":["ics","icalendar","rfc5545","calendar","export","missing-uid"],"backgroundTag":"missing-required-field","analyzedSha":"5720a38cfe95b00ca4be5016dd0d2f3195f4fa04","analyzedAt":"2026-08-21T05:07:25.672Z","schemaVersion":2},"datasetVersion":"2026-08-21T11:28:35.574Z"}