nextcloud/server · error · Sabre\DAV\Exception\NotFound

Invalid node

Error message

Invalid node

What it means

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).

Source

Thrown at apps/dav/lib/CalDAV/AppCalendar/CalendarObject.php:125

				$components[$key]->STATUS = 'CANCELLED';
				$components[$key]->SEQUENCE = isset($component->SEQUENCE) ? ((int)$component->SEQUENCE->getValue()) + 1 : 1;
				if ($component->name === 'VEVENT') {
					$components[$key]->METHOD = 'CANCEL';
				}
			}
			$this->backend->createFromString($this->getName(), (new VCalendar($components))->serialize());
		} else {
			throw new Forbidden('This calendar-object is read-only');
		}
	}

	#[\Override]
	public function getName(): string {
		// Every object is required to have an UID
		$base = $this->vobject->getBaseComponent();
		// This should never happen except the app provides invalid calendars (VEvent, VTodo... all require UID to be present)
		if ($base === null) {
			throw new NotFound('Invalid node');
		}
		if (isset($base->{'X-FILENAME'})) {
			return (string)$base->{'X-FILENAME'};
		}
		return (string)$base->UID . '.ics';
	}

	#[\Override]
	public function setName($name): void {
		throw new Forbidden('This calendar-object is read-only');
	}

	#[\Override]
	public function getLastModified(): ?int {
		$base = $this->vobject->getBaseComponent();
		if ($base !== null && $this->vobject->getBaseComponent()->{'LAST-MODIFIED'}) {
			/** @var DateTime */
			$lastModified = $this->vobject->getBaseComponent()->{'LAST-MODIFIED'};

View on GitHub (pinned to ecdeb153ff)

Solutions

  1. Fix the provider so search() returns valid iCalendar objects whose base components (VEVENT/VTODO/VJOURNAL) carry a UID
  2. Validate the provider's search() output by parsing it with Sabre\VObject\Reader in the app's tests before shipping
  3. End users: report the issue to the app that provides the calendar - the DAV layer is only the messenger

Example fix

// before: provider returns fragments without a base component
return ['UID' => 'abc', 'SUMMARY' => 'x'];

// after: include a real base component with UID
return ['UID' => 'abc', 'objects' => $veventWithData]; // ensures VCalendar has VEVENT base
Defensive patterns

Strategy: type-guard

Validate before calling

// Provider-side: validate search() output before exposing it
use Sabre\VObject\Reader;
foreach ($this->search('') as $index => $object) {
    $v = Reader::read($object['objects'] ?? '', Reader::OPTION_IGNORE_ERRORS);
    if ($v->getBaseComponent() === null) {
        $this->logger->warning('Skipping object without base component at index ' . $index);
        unset($objects[$index]);
    }
}

Type guard

function hasCalendarBaseComponent(Sabre\VObject\Component\VCalendar $v): bool {
    return $v->getBaseComponent() !== null; // VEVENT/VTODO/VJOURNAL with UID present
}

Try / catch

try {
    $name = $object->getName();
} catch (Sabre\DAV\Exception\NotFound $e) {
    // provider returned data without a base component; skip this object and report to the app
}

Prevention

When it happens

Trigger: 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.

Common situations: Buggy ICalendar implementations returning non-compliant search results; providers exposing raw data arrays missing the component keys; calendar apps updated with changed search() semantics.

Related errors


AI-assisted analysis of nextcloud/server@ecdeb153ff (2026-08-17). Data as JSON: /api/errors/9f67736173cb2822. Report an issue: GitHub.