siyuan-note/siyuan · error

CardDAV: address book not found

Error message

CardDAV: address book not found

What it means

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.

Source

Thrown at kernel/model/carddav.go:93

	defaultAddressBook = carddav.AddressBook{
		Path:                 CardDavDefaultAddressBookPath,
		Name:                 CardDavDefaultAddressBookName,
		Description:          "Default address book",
		MaxResourceSize:      addressBookMaxResourceSize,
		SupportedAddressData: addressBookSupportedAddressData,
	}
	contacts = Contacts{
		loaded:        false,
		changed:       false,
		lock:          sync.Mutex{},
		books:         sync.Map{},
		booksMetaData: []*carddav.AddressBook{},
	}

	ErrorCardDavPathInvalid = errors.New("CardDAV: path is invalid")

	ErrorCardDavBookNotFound    = errors.New("CardDAV: address book not found")
	ErrorCardDavBookPathInvalid = errors.New("CardDAV: address book path is invalid")

	ErrorCardDavAddressNotFound                 = errors.New("CardDAV: address not found")
	ErrorCardDavAddressFileExtensionNameInvalid = errors.New("CardDAV: address file extension name is invalid")
)

// ImportAddressBook imports an address book from a vCard file (*.vcf)
func ImportAddressBook(addressBookPath, cardContent string) (addresses []*AddressObject, err error) {
	// TODO: Check whether the path is valid (PathDepth: Address)
	// TODO: Check whether the address book exists
	// TODO: Decode the card content
	// TODO: Save the cards to the file system
	return
}

// ExportAddressBook exports an address book to a vCard file (*.vcf)
func ExportAddressBook(addressBookPath string) (cardContent string, err error) {
	// TODO: Check whether the path is valid (PathDepth: AddressBook)

View on GitHub (pinned to 8641553a1f)

Solutions

  1. List existing books first (query the home set /carddav/principals/main/contacts) and use one of the returned paths verbatim.
  2. Create the address book before using it (MKCOL / makeAddressBook) or switch to CardDavDefaultAddressBookPath (/carddav/principals/main/contacts/default).
  3. Compare the configured path byte-for-byte with the stored path — check trailing slashes, case, and URL encoding.
  4. Verify the book still exists in the workspace metadata file and on disk; restore it if it was deleted externally.

Example fix

// before
obj, err := contacts.PutAddressObject("/carddav/principals/main/contacts/work/c1.vcf", card, nil)
// fails: only "default" exists

// after: use an existing book (or create "work" first)
bookPath := model.CardDavDefaultAddressBookPath
obj, err := contacts.PutAddressObject(bookPath+"/c1.vcf", card, nil)
Defensive patterns

Strategy: try-catch

Validate before calling

bookPath := "/carddav/principals/main/contacts/default"
if model.GetCardDavPathDepth(bookPath) != 5 {
    return fmt.Errorf("not an address book path: %s", bookPath)
}
books, _ := contacts.ListAddressBooks()
if !slices.ContainsFunc(books, func(b *carddav.AddressBook) bool { return b.Path == bookPath }) {
    return fmt.Errorf("address book %s does not exist yet", bookPath)
}

Type guard

func addressBookExists(books []*carddav.AddressBook, p string) bool {
    for _, b := range books {
        if b.Path == p { return true }
    }
    return false
}

Try / catch

if err := contacts.PutAddressObject(bookPath+"/c1.vcf", card, nil); err != nil {
    if errors.Is(err, model.ErrorCardDavBookNotFound) {
        if _, mkErr := contacts.CreateAddressBook(bookPath, "work"); mkErr != nil {
            return mkErr
        }
        // retry the PUT once
    }
}

Prevention

When it happens

Trigger: 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.

Common situations: 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.

Understand the failure class

Background: 'Could not be found', 'does not exist', 'not found in database': the resource-not-found family when an ID, slug, key, or URI lookup comes back empty — this error's family across 20 libraries.

Related errors


AI-assisted analysis of siyuan-note/siyuan@8641553a1f (2026-09-11). Data as JSON: /api/errors/c6392f4df98557d5. Report an issue: GitHub.