nextcloud/server · warning · Forbidden

Permission denied to create files in the trashbin

Error message

Permission denied to create files in the trashbin

What it means

TrashbinHome is the 'trashbin' collection at calendars/<principal>/trashbin/. Its createFile() unconditionally throws this Forbidden (HTTP 403): files cannot be created directly in the trashbin root. The trashbin only has the fixed children 'restore' (RestoreTarget) and 'objects' (DeletedCalendarObjectsCollection), and its content is produced exclusively by the server when calendar objects are deleted.

Source

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

				'principal' => $ownerPrincipal,
				'protected' => true,
			],
			[
				'privilege' => '{DAV:}all',
				'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
				);
		}

View on GitHub (pinned to ecdeb153ff)

Solutions

  1. Never PUT to calendars/<user>/trashbin/ or its children; write new events into the target calendar collection (calendars/<user>/<calendar>/<uri>.ics) instead
  2. Make clients treat the trashbin subtree (trashbin, trashbin/objects, trashbin/restore) as read-only system state
  3. In tests, populate the trashbin by deleting a real calendar object rather than uploading

Example fix

# before: upload into the trashbin root (403)
PUT /remote.php/dav/calendars/alice/trashbin/event.ics

# after: upload into the calendar; delete it afterwards if trashbin state is needed
PUT /remote.php/dav/calendars/alice/personal/event.ics
DELETE /remote.php/dav/calendars/alice/personal/event.ics
Defensive patterns

Strategy: validation

Validate before calling

const isTrashbinRoot = (href) => /\/trashbin\/?$/.test(href) || href.includes('/trashbin/');
if (!isTrashbinRoot(destHref)) {
    await client.put(destHref, icalData);
}

Type guard

function isTrashbinHref(href: string): boolean {
  return href.includes('/trashbin');
}

Prevention

When it happens

Trigger: A PUT to /remote.php/dav/calendars/<principal>/trashbin/<filename> attempting to upload into the trashbin root; clients resolving a 404 for an unknown trashbin child by trying to create it.

Common situations: File-manager style clients treating the trashbin as a writable folder; sync tools uploading a full local tree including special folders; automated tests probing writability of system collections.

Related errors


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