phacility/phabricator · error · Exception
Unable to write ICS document: event has no modified time, bu
Error message
Unable to write ICS document: event has no modified time, but each event MUST have a modified time.
What it means
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.
Source
Thrown at src/applications/calendar/parser/ics/PhutilICSWriter.php:163
throw new Exception(
pht(
'Unable to write ICS document: event has no UID, but each event '.
'MUST have a UID.'));
}
$properties[] = $this->newTextProperty(
'UID',
$uid);
$created = $event->getCreatedDateTime();
if ($created) {
$properties[] = $this->newDateTimeProperty(
'CREATED',
$event->getCreatedDateTime());
}
$dtstamp = $event->getModifiedDateTime();
if (!$dtstamp) {
throw new Exception(
pht(
'Unable to write ICS document: event has no modified time, but '.
'each event MUST have a modified time.'));
}
$properties[] = $this->newDateTimeProperty(
'DTSTAMP',
$dtstamp);
$dtstart = $event->getStartDateTime();
if ($dtstart) {
$properties[] = $this->newDateTimeProperty(
'DTSTART',
$dtstart);
}
$dtend = $event->getEndDateTime();
if ($dtend) {
$properties[] = $this->newDateTimeProperty(View on GitHub (pinned to 5720a38cfe)
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
Example fix
// before
$ics = PhutilICSWriter::writeDocuments(array($event));
// after
if (!$event->getModifiedDateTime()) {
$event->setModifiedDateTime(
PhutilCalendarAbsoluteDateTime::newFromEpoch(time()));
}
$ics = PhutilICSWriter::writeDocuments(array($event)); Defensive patterns
Strategy: validation
Validate before calling
foreach ($events as $event) {
if (!$event->getModifiedDateTime()) {
$event->setModifiedDateTime(
PhutilCalendarAbsoluteDateTime::newFromEpoch(time()));
}
}
$ics = PhutilICSWriter::writeDocuments($events); Try / catch
try {
$ics = PhutilICSWriter::writeDocuments(array($event));
} catch (Exception $ex) {
if (strpos($ex->getMessage(), 'modified time') !== false) {
$event->setModifiedDateTime(
PhutilCalendarAbsoluteDateTime::newFromEpoch(time()));
$ics = PhutilICSWriter::writeDocuments(array($event));
} else {
throw $ex;
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Understand the failure class
Background: "Missing required field" and "field is required" errors: why libraries reject payloads that omit mandatory fields — this error's family across 20 libraries.
Related errors
- Unable to write ICS document: event has no UID, but each eve
- RRULE BYDAY value "%s" has an offset with magnitude "%s", bu
- RRULE specifies BYMONTHDAY with FREQ set to WEEKLY, which vi
- RRULE specifies BYYEARDAY with FREQ of DAILY, WEEKLY or MONT
- Value "%s" in RRULE "%s" parameter is invalid: it must be be
AI-assisted analysis of phacility/phabricator@5720a38cfe (2026-08-21).
Data as JSON: /api/errors/b66ecb5a52164bea.
Report an issue: GitHub.