{"record":{"id":"c6392f4df98557d5","repo":"siyuan-note/siyuan","slug":"carddav-address-book-not-found","errorCode":null,"errorMessage":"CardDAV: address book not found","messagePattern":"CardDAV: address book not found","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/carddav.go","lineNumber":93,"sourceCode":"\n\tdefaultAddressBook = carddav.AddressBook{\n\t\tPath:                 CardDavDefaultAddressBookPath,\n\t\tName:                 CardDavDefaultAddressBookName,\n\t\tDescription:          \"Default address book\",\n\t\tMaxResourceSize:      addressBookMaxResourceSize,\n\t\tSupportedAddressData: addressBookSupportedAddressData,\n\t}\n\tcontacts = Contacts{\n\t\tloaded:        false,\n\t\tchanged:       false,\n\t\tlock:          sync.Mutex{},\n\t\tbooks:         sync.Map{},\n\t\tbooksMetaData: []*carddav.AddressBook{},\n\t}\n\n\tErrorCardDavPathInvalid = errors.New(\"CardDAV: path is invalid\")\n\n\tErrorCardDavBookNotFound    = errors.New(\"CardDAV: address book not found\")\n\tErrorCardDavBookPathInvalid = errors.New(\"CardDAV: address book path is invalid\")\n\n\tErrorCardDavAddressNotFound                 = errors.New(\"CardDAV: address not found\")\n\tErrorCardDavAddressFileExtensionNameInvalid = errors.New(\"CardDAV: address file extension name is invalid\")\n)\n\n// ImportAddressBook imports an address book from a vCard file (*.vcf)\nfunc ImportAddressBook(addressBookPath, cardContent string) (addresses []*AddressObject, err error) {\n\t// TODO: Check whether the path is valid (PathDepth: Address)\n\t// TODO: Check whether the address book exists\n\t// TODO: Decode the card content\n\t// TODO: Save the cards to the file system\n\treturn\n}\n\n// ExportAddressBook exports an address book to a vCard file (*.vcf)\nfunc ExportAddressBook(addressBookPath string) (cardContent string, err error) {\n\t// TODO: Check whether the path is valid (PathDepth: AddressBook)","sourceCodeStart":75,"sourceCodeEnd":111,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/carddav.go#L75-L111","documentation":"ErrorCardDavBookNotFound is thrown by the Contacts store (GetAddressBook, ListAddressObjects, PutAddressObject, GetAddress, DeleteAddress — kernel/model/carddav.go:273,297 and related) when the address-book path parses to a valid depth but no AddressBook with that exact path is registered in the books sync.Map. The path is structurally valid but the collection does not exist, so the operation cannot proceed. It corresponds to HTTP 404 on an address-book collection URL.","triggerScenarios":"Any Contacts method receives an address book path (e.g. /carddav/principals/main/contacts/<name>) whose books.Load(path) misses: the book was never created, was deleted on another client, only the 'default' book exists and a custom one was requested, or the name differs by case/trailing slash from what load() registered.","commonSituations":"A client configured with an address book URL that points to a book removed from address-books.json or from disk; using a book name that was never created (only 'default' exists out of the box); stale cached collection URLs after the workspace or metadata file changed; typo in book path in scripts.","solutions":["List existing books first (query the home set /carddav/principals/main/contacts) and use one of the returned paths verbatim.","Create the address book before using it (MKCOL / makeAddressBook) or switch to CardDavDefaultAddressBookPath (/carddav/principals/main/contacts/default).","Compare the configured path byte-for-byte with the stored path — check trailing slashes, case, and URL encoding.","Verify the book still exists in the workspace metadata file and on disk; restore it if it was deleted externally."],"exampleFix":"// before\nobj, err := contacts.PutAddressObject(\"/carddav/principals/main/contacts/work/c1.vcf\", card, nil)\n// fails: only \"default\" exists\n\n// after: use an existing book (or create \"work\" first)\nbookPath := model.CardDavDefaultAddressBookPath\nobj, err := contacts.PutAddressObject(bookPath+\"/c1.vcf\", card, nil)","handlingStrategy":"try-catch","validationCode":"bookPath := \"/carddav/principals/main/contacts/default\"\nif model.GetCardDavPathDepth(bookPath) != 5 {\n    return fmt.Errorf(\"not an address book path: %s\", bookPath)\n}\nbooks, _ := contacts.ListAddressBooks()\nif !slices.ContainsFunc(books, func(b *carddav.AddressBook) bool { return b.Path == bookPath }) {\n    return fmt.Errorf(\"address book %s does not exist yet\", bookPath)\n}","typeGuard":"func addressBookExists(books []*carddav.AddressBook, p string) bool {\n    for _, b := range books {\n        if b.Path == p { return true }\n    }\n    return false\n}","tryCatchPattern":"if err := contacts.PutAddressObject(bookPath+\"/c1.vcf\", card, nil); err != nil {\n    if errors.Is(err, model.ErrorCardDavBookNotFound) {\n        if _, mkErr := contacts.CreateAddressBook(bookPath, \"work\"); mkErr != nil {\n            return mkErr\n        }\n        // retry the PUT once\n    }\n}","preventionTips":["Discover address books dynamically (propfind on the home set) instead of hard-coding book names.","Remember only the 'default' book exists out of the box; create custom books before first use.","Compare book paths exactly (no trailing slash, consistent case) when configuring clients.","Handle the not-found error as 404 and offer MKCOL-style creation in client sync logic."],"tags":["carddav","address-book","not-found","webdav","contacts"],"backgroundTag":"resource-not-found","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T00:17:10.932Z"}