{"record":{"id":"3745aeede8ec6bad","repo":"siyuan-note/siyuan","slug":"carddav-path-is-invalid","errorCode":null,"errorMessage":"CardDAV: path is invalid","messagePattern":"CardDAV: path is invalid","errorType":"error_code","errorClass":null,"httpStatus":null,"severity":"error","filePath":"kernel/model/carddav.go","lineNumber":91,"sourceCode":"\t\t},\n\t}\n\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)","sourceCodeStart":73,"sourceCodeEnd":109,"githubUrl":"https://github.com/siyuan-note/siyuan/blob/8641553a1f07374001902d3ce773285db1292b2d/kernel/model/carddav.go#L73-L109","documentation":"ErrorCardDavPathInvalid is a sentinel error returned by Contacts.QueryAddressObjects (kernel/model/carddav.go:484) when the queried CardDAV URL's path depth does not correspond to any known resource type (root, principals, user principal, home set, address book, or address). It is the catch-all 'this URL is not a valid CardDAV resource' error, distinct from the more specific book/address errors.","triggerScenarios":"QueryAddressObjects(urlPath, query) receives a path whose GetCardDavPathDepth is not 1..6 of the known /carddav/principals/main/contacts... hierarchy — e.g. a too-deep path (nested segment below an address), an empty or cleaned path resolving to depth 0, or an address-book.json metadata path passed by mistake.","commonSituations":"A CardDAV client misconfigured with a wrong base URL (extra or missing path segments, trailing garbage, double slashes that clean differently); integration code constructing ad-hoc paths under /carddav; a client pointing at the CalDAV hierarchy paths inside the CardDAV handler.","solutions":["Print GetCardDavPathDepth(urlPath) and compare against the constant hierarchy: /carddav (1), /carddav/principals (2), /carddav/principals/main (3), /carddav/principals/main/contacts (4), .../contacts/<book> (5), .../contacts/<book>/<id>.vcf (6).","Fix the client's address-book base URL so it matches CardDavHomeSetPath (/carddav/principals/main/contacts) or one of its sub-resources.","Remove extraneous segments (nesting below an address, metadata file names) from the queried path.","Query a listed resource instead: enumerate address books from the home set first and use those exact paths."],"exampleFix":"// before: nested path of depth 7 -> no known resource type\nbooks, err := contacts.QueryAddressObjects(\"/carddav/principals/main/contacts/default/sub/x.vcf\", nil)\n\n// after: query the address book itself\nbooks, err := contacts.QueryAddressObjects(\"/carddav/principals/main/contacts/default\", nil)","handlingStrategy":"validation","validationCode":"depth := int(model.GetCardDavPathDepth(urlPath))\nif depth < 1 || depth > 6 {\n    return fmt.Errorf(\"CardDAV path depth %d out of range for %s\", depth, urlPath)\n}","typeGuard":"func isKnownCardDavResource(p string) bool {\n    d := model.GetCardDavPathDepth(p)\n    return d >= model.CardDavPathDepth(1) && d <= model.CardDavPathDepth(6)\n}","tryCatchPattern":"objs, err := contacts.QueryAddressObjects(urlPath, query)\nif errors.Is(err, model.ErrorCardDavPathInvalid) {\n    log.Printf(\"unknown CardDAV resource %s (depth %d)\", urlPath, model.GetCardDavPathDepth(urlPath))\n    return http.StatusNotFound\n}","preventionTips":["Configure clients with the canonical base paths: /carddav/principals/main/contacts or a sub-book of it.","Sanitize incoming URLs (clean, collapse duplicate slashes, strip query/fragment) before path-depth checks.","Log GetCardDavPathDepth alongside the raw URL when rejecting requests to speed up diagnosis.","Cover all six depth levels in tests for query/list handlers so unknown depths fail loudly in CI."],"tags":["carddav","path","validation","webdav","contacts"],"backgroundTag":"invalid-argument-format","analyzedSha":"8641553a1f07374001902d3ce773285db1292b2d","analyzedAt":"2026-09-11T16:08:28.414Z","contentChangedAt":"2026-09-11T16:08:28.414Z","schemaVersion":2},"datasetVersion":"2026-09-14T05:17:10.506Z"}