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

Creating a new entry is not allowed

Error message

Creating a new entry is not allowed

What it means

Creating a new object inside an app calendar collection calls AppCalendar::createFile(); it only succeeds when the backing ICalendar provider also implements OCP\Calendar\ICreateFromString. If it does not (read-only provider), the write is refused with Forbidden (HTTP 403) 'Creating a new entry is not allowed'.

Source

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

		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 [
			'{DAV:}displayname' => $this->calendar->getDisplayName() ?: $this->calendar->getKey(),
			'{http://apple.com/ns/ical/}calendar-color' => $this->calendar->getDisplayColor() ?: '#0082c9',
			'{' . Plugin::NS_CALDAV . '}supported-calendar-component-set' => new SupportedCalendarComponentSet(['VEVENT', 'VJOURNAL', 'VTODO']),
		];
	}

	#[\Override]
	public function calendarQuery(array $filters) {
		$result = [];
		$objects = $this->getChildren();

		foreach ($objects as $object) {

View on GitHub (pinned to ecdeb153ff)

Solutions

  1. Verify the calendar provider class implements OCP\Calendar\ICreateFromString; without it every write path is 403 by design
  2. App developers: implement createFromString() on the provider to enable CalDAV writes
  3. Clients: check the calendar's permissions/ACL before syncing changes; skip writes when PERMISSION_CREATE is absent

Example fix

// before
class MyCalendarProvider implements ICalendar { /* read-only over CalDAV */ }

// after
class MyCalendarProvider implements ICalendar, ICreateFromString {
    public function createFromString(string $name, ?string $data = null): void { /* persist */ }
}
Defensive patterns

Strategy: type-guard

Validate before calling

// App-side: only promise CalDAV writes when the provider supports them
$calendar = \OC::$server->get(\OCP\Calendar\IManager::class)->getCalendars()[0];
$writable = $calendar instanceof \OCP\Calendar\ICreateFromString
    && ($calendar->getPermissions() & \OCP\Constants::PERMISSION_CREATE) !== 0;

Type guard

function acceptsCalDavWrites(\OCP\Calendar\ICalendar $c): bool {
    return $c instanceof \OCP\Calendar\ICreateFromString
        && ($c->getPermissions() & \OCP\Constants::PERMISSION_CREATE) !== 0;
}

Try / catch

try {
    $node->createFile($name, $data);
} catch (Sabre\DAV\Exception\Forbidden $e) {
    // read-only app calendar; fall back to read-only sync mode
}

Prevention

When it happens

Trigger: PUT/POST creating a new .ics object in an app-registered calendar whose provider class implements only ICalendar (read-only), e.g. a holiday or feed calendar.

Common situations: Sync clients attempting two-way sync against read-only app calendars; app authors who forgot that write support requires implementing ICreateFromString (AppCalendar::getPermissions() then also drops to PERMISSION_READ).

Related errors


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