{"record":{"id":"dd0107ff9f972497","repo":"nextcloud/server","slug":"renaming-address-books-is-not-yet-supported","errorCode":null,"errorMessage":"Renaming address books is not yet supported","messagePattern":"Renaming address books is not yet supported","errorType":"http","errorClass":"Sabre\\DAV\\Exception\\MethodNotAllowed","httpStatus":405,"severity":"warning","filePath":"apps/dav/lib/CardDAV/Integration/ExternalAddressBook.php","lineNumber":59,"sourceCode":"\n\t/**\n\t * @inheritDoc\n\t */\n\t#[\\Override]\n\tfinal public function getName() {\n\t\treturn implode(self::DELIMITER, [\n\t\t\tself::PREFIX,\n\t\t\t$this->appId,\n\t\t\t$this->uri,\n\t\t]);\n\t}\n\n\t/**\n\t * @inheritDoc\n\t */\n\t#[\\Override]\n\tfinal public function setName($name) {\n\t\tthrow new DAV\\Exception\\MethodNotAllowed('Renaming address books is not yet supported');\n\t}\n\n\t/**\n\t * @inheritDoc\n\t */\n\t#[\\Override]\n\tfinal public function createDirectory($name) {\n\t\tthrow new DAV\\Exception\\MethodNotAllowed('Creating collections in address book objects is not allowed');\n\t}\n\n\t/**\n\t * Checks whether the address book uri is app-generated\n\t *\n\t * @param string $uri\n\t *\n\t * @return bool\n\t */\n\tpublic static function isAppGeneratedAddressBook(string $uri): bool {","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/nextcloud/server/blob/ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3/apps/dav/lib/CardDAV/Integration/ExternalAddressBook.php#L41-L77","documentation":"HTTP 405 MethodNotAllowed from the final ExternalAddressBook::setName(), which always throws. ExternalAddressBook is the CardDAV counterpart of ExternalCalendar: apps extend it to expose app-backed address books with URIs prefixed app-generated--<appId>--. Sabre calls setName() on MOVE, so renaming an app-registered address book is rejected by design.","triggerScenarios":"A CardDAV client issuing MOVE on /remote.php/dav/addressbooks/<user>/app-generated--<appId>--<uri>/ — rename attempts from contact clients or sync tools that rename every writable-looking collection.","commonSituations":"Users renaming address books contributed by apps (e.g. app-provided contact backends); automation that normalizes collection names during migration.","solutions":["Rename or reconfigure the address book inside the app that provides it","Do not issue DAV MOVE on address books whose URI starts with app-generated--","If you are the provider app, change the exposed uri and let clients re-discover"],"exampleFix":"// before: MOVE an app-generated address book -> 405\nawait davMove(`/remote.php/dav/addressbooks/${uid}/${bookUri}/`, `/remote.php/dav/addressbooks/${uid}/${newUri}/`);\n// after: skip app-generated address books when renaming\nif (bookUri.startsWith('app-generated--')) { continue; }\nawait davMove(`/remote.php/dav/addressbooks/${uid}/${bookUri}/`, `/remote.php/dav/addressbooks/${uid}/${newUri}/`);","handlingStrategy":"validation","validationCode":"// client: filter rename targets\nfunction isRenamableAddressBook(bookUri) {\n  return !bookUri.startsWith('app-generated--');\n}","typeGuard":"// PHP (server-side)\nfunction isRenamable(\\Sabre\\DAV\\INode $node): bool {\n  if ($node instanceof \\OCA\\DAV\\CardDAV\\Integration\\ExternalAddressBook) {\n    return false;\n  }\n  return !str_starts_with($node->getName(), 'app-generated--');\n}","tryCatchPattern":"try {\n  await davMove(src, dst);\n} catch (e) {\n  if (e.status === 405) { /* renaming unsupported: manage via provider app */ }\n  else throw e;\n}","preventionTips":["Exclude app-generated-- address books from rename flows","Configure external address books in the providing app"],"tags":["carddav","external-addressbook","method-not-allowed","move","app-integration"],"backgroundTag":"unsupported-operation","analyzedSha":"ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3","analyzedAt":"2026-08-17T01:36:13.386Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}