nextcloud/server · warning · Sabre\DAV\Exception\Forbidden

Deleting an entry is not implemented

Error message

Deleting an entry is not implemented

What it means

DELETE on an app-provided calendar collection always throws Forbidden (HTTP 403): OCP\Calendar\ICalendar has no deletion API, so AppCalendar::delete() cannot be implemented. Removing such a calendar means acting in the source app (uninstall it, disable the provider, or revoke access).

Solutions

  1. Remove or stop the calendar in the providing app's settings instead of via CalDAV
  2. Clients: when a calendar DELETE returns 403, mark the calendar as non-deletable instead of retrying
  3. App providers: offer deletion in the app UI so users have a supported path
Defensive patterns

Strategy: fallback

Validate before calling

if ($calendarNode instanceof \OCA\DAV\CalDAV\AppCalendar\AppCalendar) {
    // no CalDAV delete for app calendars - act in the app instead
    return false;
}

Type guard

function isDeletableCalendar(Sabre\DAV\INode $node): bool {
    return !($node instanceof \OCA\DAV\CalDAV\AppCalendar\AppCalendar);
}

Try / catch

try {
    $node->delete();
} catch (Sabre\DAV\Exception\Forbidden $e) {
    // app-owned calendar: direct the user to the providing app
}

Prevention

When it happens

Trigger: A CalDAV client sending DELETE to the collection URI of an app-registered calendar, e.g. DELETE /remote.php/dav/calendars/user/appid-calendarname/

Common situations: Users trying to remove app-backed calendars (task providers, embedded/feed calendars) from Apple Calendar, Thunderbird or DAVx5 instead of inside the Nextcloud app that provides them.

Related errors


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

Appendix: source

Thrown at apps/dav/lib/CalDAV/AppCalendar/AppCalendar.php:104

		throw new Forbidden('Setting ACL is not supported on this node');
	}

	#[\Override]
	public function getSupportedPrivilegeSet(): ?array {
		// Use the default one
		return null;
	}

	#[\Override]
	public function getLastModified(): ?int {
		// unknown
		return null;
	}

	#[\Override]
	public function delete(): void {
		// No method for deleting a calendar in OCP\Calendar\ICalendar
		throw new Forbidden('Deleting an entry is not implemented');
	}

	#[\Override]
	public function createFile($name, $data = null) {
		if ($this->calendar instanceof ICreateFromString) {
			if (is_resource($data)) {
				$data = stream_get_contents($data) ?: null;
			}
			$this->calendar->createFromString($name, is_null($data) ? '' : $data);
			return null;
		} else {
			throw new Forbidden('Creating a new entry is not allowed');
		}
	}

	#[\Override]
	public function getProperties($properties) {
		return [

View on GitHub (pinned to ecdeb153ff)