{"record":{"id":"b4d0d70e7ba4b4bc","repo":"nextcloud/server","slug":"permission-denied-to-create-files-in-the-trashbin","errorCode":null,"errorMessage":"Permission denied to create files in the trashbin","messagePattern":"Permission denied to create files in the trashbin","errorType":"exception","errorClass":"Forbidden","httpStatus":403,"severity":"warning","filePath":"apps/dav/lib/CalDAV/Trashbin/TrashbinHome.php","lineNumber":65,"sourceCode":"\t\t\t\t'principal' => $ownerPrincipal,\n\t\t\t\t'protected' => true,\n\t\t\t],\n\t\t\t[\n\t\t\t\t'privilege' => '{DAV:}all',\n\t\t\t\t'principal' => $ownerPrincipal . '/calendar-proxy-write',\n\t\t\t\t'protected' => true,\n\t\t\t],\n\t\t\t[\n\t\t\t\t'privilege' => '{DAV:}read',\n\t\t\t\t'principal' => $ownerPrincipal . '/calendar-proxy-read',\n\t\t\t\t'protected' => true,\n\t\t\t],\n\t\t];\n\t}\n\n\t#[\\Override]\n\tpublic function createFile($name, $data = null) {\n\t\tthrow new Forbidden('Permission denied to create files in the trashbin');\n\t}\n\n\t#[\\Override]\n\tpublic function createDirectory($name) {\n\t\tthrow new Forbidden('Permission denied to create a directory in the trashbin');\n\t}\n\n\t#[\\Override]\n\tpublic function getChild($name): INode {\n\t\tswitch ($name) {\n\t\t\tcase RestoreTarget::NAME:\n\t\t\t\treturn new RestoreTarget();\n\t\t\tcase DeletedCalendarObjectsCollection::NAME:\n\t\t\t\treturn new DeletedCalendarObjectsCollection(\n\t\t\t\t\t$this->caldavBackend,\n\t\t\t\t\t$this->principalInfo\n\t\t\t\t);\n\t\t}","sourceCodeStart":47,"sourceCodeEnd":83,"githubUrl":"https://github.com/nextcloud/server/blob/ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3/apps/dav/lib/CalDAV/Trashbin/TrashbinHome.php#L47-L83","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Never PUT to calendars/<user>/trashbin/ or its children; write new events into the target calendar collection (calendars/<user>/<calendar>/<uri>.ics) instead","Make clients treat the trashbin subtree (trashbin, trashbin/objects, trashbin/restore) as read-only system state","In tests, populate the trashbin by deleting a real calendar object rather than uploading"],"exampleFix":"# before: upload into the trashbin root (403)\nPUT /remote.php/dav/calendars/alice/trashbin/event.ics\n\n# after: upload into the calendar; delete it afterwards if trashbin state is needed\nPUT /remote.php/dav/calendars/alice/personal/event.ics\nDELETE /remote.php/dav/calendars/alice/personal/event.ics","handlingStrategy":"validation","validationCode":"const isTrashbinRoot = (href) => /\\/trashbin\\/?$/.test(href) || href.includes('/trashbin/');\nif (!isTrashbinRoot(destHref)) {\n    await client.put(destHref, icalData);\n}","typeGuard":"function isTrashbinHref(href: string): boolean {\n  return href.includes('/trashbin');\n}","tryCatchPattern":null,"preventionTips":["Allow writes only to calendar collections identified via PROPFIND resourcetype","Keep one shared path guard that blocks PUT/MKCOL for the whole trashbin subtree","Populate trashbins in tests by deleting live objects, never by uploading"],"tags":["caldav","trashbin","http-403","webdav","read-only"],"backgroundTag":"webdav-403-forbidden","analyzedSha":"ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3","analyzedAt":"2026-08-17T01:36:13.386Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}