nextcloud/server · error · Forbidden

Permission denied to create a directory in the trashbin

Error message

Permission denied to create a directory in the trashbin

What it means

TrashbinHome is the CalDAV trashbin collection at /remote.php/dav/calendars/<principal>/trashbin. It implements Sabre\DAV\ICollection but is deliberately read-only: deleted-calendar storage cannot be written to directly, and the only valid children are 'restore' and 'objects'. createDirectory() therefore throws Sabre\DAV\Exception\Forbidden unconditionally, which the DAV server maps to HTTP 403 regardless of the caller's ACL privileges.

Source

Thrown at apps/dav/lib/CalDAV/Trashbin/TrashbinHome.php:70

				'principal' => $ownerPrincipal . '/calendar-proxy-write',
				'protected' => true,
			],
			[
				'privilege' => '{DAV:}read',
				'principal' => $ownerPrincipal . '/calendar-proxy-read',
				'protected' => true,
			],
		];
	}

	#[\Override]
	public function createFile($name, $data = null) {
		throw new Forbidden('Permission denied to create files in the trashbin');
	}

	#[\Override]
	public function createDirectory($name) {
		throw new Forbidden('Permission denied to create a directory in the trashbin');
	}

	#[\Override]
	public function getChild($name): INode {
		switch ($name) {
			case RestoreTarget::NAME:
				return new RestoreTarget();
			case DeletedCalendarObjectsCollection::NAME:
				return new DeletedCalendarObjectsCollection(
					$this->caldavBackend,
					$this->principalInfo
				);
		}

		throw new NotFound();
	}

	#[\Override]

View on GitHub (pinned to ecdeb153ff)

Solutions

  1. Create calendars under the calendar home /remote.php/dav/calendars/<user>/, never under trashbin/
  2. To undelete, MOVE the object from trashbin/objects/ into trashbin/restore/ instead of creating collections
  3. Treat 403 on trashbin mutations as expected behavior and do not retry them

Example fix

// before
MKCOL /remote.php/dav/calendars/alice/trashbin/holidays/
-> 403 Permission denied to create a directory in the trashbin

// after
MKCOL /remote.php/dav/calendars/alice/holidays/
-> 201 Created
Defensive patterns

Strategy: validation

Validate before calling

// the trashbin is a read-only sibling of the calendar home
$isTrashbin = str_contains(rtrim($requestUri, '/') . '/', "/remote.php/dav/calendars/{$userId}/trashbin/");
if ($isTrashbin && $method === 'MKCOL') {
    throw new LogicException('The CalDAV trashbin is read-only; create calendars under the calendar home');
}

Try / catch

try {
    $client->request('MKCOL', $uri);
} catch (\Sabre\HTTP\ClientHttpException $e) {
    if ($e->getResponse()->getStatus() === 403) {
        return; // read-only trashbin: skip, never retry
    }
    throw $e;
}

Prevention

When it happens

Trigger: An MKCOL request whose Request-URI is /remote.php/dav/calendars/<user>/trashbin/<name>; a client that discovers the trashbin via PROPFIND and then offers 'new calendar' on it; code that reuses a calendar-home URL builder for the trashbin path.

Common situations: Custom sync or test tooling that blanket-issues MKCOL on every discovered collection; URL mixups between the calendar home (.../calendars/<user>/) and its trashbin sibling (.../calendars/<user>/trashbin/); scripts trying to seed deleted data directly.

Related errors


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