{"record":{"id":"9f67736173cb2822","repo":"nextcloud/server","slug":"invalid-node","errorCode":null,"errorMessage":"Invalid node","messagePattern":"Invalid node","errorType":"exception","errorClass":"Sabre\\DAV\\Exception\\NotFound","httpStatus":404,"severity":"error","filePath":"apps/dav/lib/CalDAV/AppCalendar/CalendarObject.php","lineNumber":125,"sourceCode":"\t\t\t\t$components[$key]->STATUS = 'CANCELLED';\n\t\t\t\t$components[$key]->SEQUENCE = isset($component->SEQUENCE) ? ((int)$component->SEQUENCE->getValue()) + 1 : 1;\n\t\t\t\tif ($component->name === 'VEVENT') {\n\t\t\t\t\t$components[$key]->METHOD = 'CANCEL';\n\t\t\t\t}\n\t\t\t}\n\t\t\t$this->backend->createFromString($this->getName(), (new VCalendar($components))->serialize());\n\t\t} else {\n\t\t\tthrow new Forbidden('This calendar-object is read-only');\n\t\t}\n\t}\n\n\t#[\\Override]\n\tpublic function getName(): string {\n\t\t// Every object is required to have an UID\n\t\t$base = $this->vobject->getBaseComponent();\n\t\t// This should never happen except the app provides invalid calendars (VEvent, VTodo... all require UID to be present)\n\t\tif ($base === null) {\n\t\t\tthrow new NotFound('Invalid node');\n\t\t}\n\t\tif (isset($base->{'X-FILENAME'})) {\n\t\t\treturn (string)$base->{'X-FILENAME'};\n\t\t}\n\t\treturn (string)$base->UID . '.ics';\n\t}\n\n\t#[\\Override]\n\tpublic function setName($name): void {\n\t\tthrow new Forbidden('This calendar-object is read-only');\n\t}\n\n\t#[\\Override]\n\tpublic function getLastModified(): ?int {\n\t\t$base = $this->vobject->getBaseComponent();\n\t\tif ($base !== null && $this->vobject->getBaseComponent()->{'LAST-MODIFIED'}) {\n\t\t\t/** @var DateTime */\n\t\t\t$lastModified = $this->vobject->getBaseComponent()->{'LAST-MODIFIED'};","sourceCodeStart":107,"sourceCodeEnd":143,"githubUrl":"https://github.com/nextcloud/server/blob/ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3/apps/dav/lib/CalDAV/AppCalendar/CalendarObject.php#L107-L143","documentation":"CalendarObject::getName() derives the object name from the VCalendar's base component (X-FILENAME if set, otherwise UID.ics). getBaseComponent() returning null means the VCalendar built from the app backend's search() results contains no VEVENT/VTODO/VJOURNAL - invalid calendar data from the provider, as the inline comment states ('should never happen except the app provides invalid calendars'). Thrown as NotFound (HTTP 404).","triggerScenarios":"An app calendar provider whose search() returns objects without a base component (e.g. only VTIMEZONE definitions or malformed fragments), so any CalDAV operation that resolves the node's name (getChild, PROPFIND on children, PUT handling) fails.","commonSituations":"Buggy ICalendar implementations returning non-compliant search results; providers exposing raw data arrays missing the component keys; calendar apps updated with changed search() semantics.","solutions":["Fix the provider so search() returns valid iCalendar objects whose base components (VEVENT/VTODO/VJOURNAL) carry a UID","Validate the provider's search() output by parsing it with Sabre\\VObject\\Reader in the app's tests before shipping","End users: report the issue to the app that provides the calendar - the DAV layer is only the messenger"],"exampleFix":"// before: provider returns fragments without a base component\nreturn ['UID' => 'abc', 'SUMMARY' => 'x'];\n\n// after: include a real base component with UID\nreturn ['UID' => 'abc', 'objects' => $veventWithData]; // ensures VCalendar has VEVENT base","handlingStrategy":"type-guard","validationCode":"// Provider-side: validate search() output before exposing it\nuse Sabre\\VObject\\Reader;\nforeach ($this->search('') as $index => $object) {\n    $v = Reader::read($object['objects'] ?? '', Reader::OPTION_IGNORE_ERRORS);\n    if ($v->getBaseComponent() === null) {\n        $this->logger->warning('Skipping object without base component at index ' . $index);\n        unset($objects[$index]);\n    }\n}","typeGuard":"function hasCalendarBaseComponent(Sabre\\VObject\\Component\\VCalendar $v): bool {\n    return $v->getBaseComponent() !== null; // VEVENT/VTODO/VJOURNAL with UID present\n}","tryCatchPattern":"try {\n    $name = $object->getName();\n} catch (Sabre\\DAV\\Exception\\NotFound $e) {\n    // provider returned data without a base component; skip this object and report to the app\n}","preventionTips":["App providers: guarantee every search() result contains a base component with a UID","Parse provider output with Sabre\\VObject\\Reader in tests to catch invalid iCalendar early","Filter invalid objects out before they reach the DAV layer instead of letting 404s surface"],"tags":["php","webdav","caldav","nextcloud","app-calendar","icalendar","invalid-data"],"backgroundTag":"invalid-icalendar-data","analyzedSha":"ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3","analyzedAt":"2026-08-17T01:36:13.386Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}