nextcloud/server · error · NotFound

Card not found

Error message

Card not found

What it means

AddressBook::getChild($name) resolves a card by URI through CardDavBackend::getCard(addressBookId, name); when no row matches it throws NotFound('Card not found'), which the DAV server returns as HTTP 404. This is the standard 'no such resource' answer for GET/PROPFIND on /remote.php/dav/addressbooks/<...>/<book>/<card>.vcf.

Source

Thrown at apps/dav/lib/CardDAV/AddressBook.php:157

		}

		$acl = $this->carddavBackend->applyShareAcl($this->getResourceId(), $acl);
		$allowedPrincipals = [$this->getOwner(), parent::getOwner(), 'principals/system/system', '{DAV:}authenticated'];
		return array_filter($acl, function ($rule) use ($allowedPrincipals) {
			return \in_array($rule['principal'], $allowedPrincipals, true);
		});
	}

	#[\Override]
	public function getChildACL() {
		return $this->getACL();
	}

	#[\Override]
	public function getChild($name) {
		$obj = $this->carddavBackend->getCard($this->addressBookInfo['id'], $name);
		if (!$obj) {
			throw new NotFound('Card not found');
		}
		$obj['acl'] = $this->getChildACL();
		return new Card($this->carddavBackend, $this->addressBookInfo, $obj);
	}

	#[\Override]
	public function getChildren() {
		$objs = $this->carddavBackend->getCards($this->addressBookInfo['id']);
		$children = [];
		foreach ($objs as $obj) {
			$obj['acl'] = $this->getChildACL();
			$children[] = new Card($this->carddavBackend, $this->addressBookInfo, $obj);
		}

		return $children;
	}

	#[\Override]

View on GitHub (pinned to ecdeb153ff)

Solutions

  1. Treat the 404 as 'card gone': remove it from the local cache and continue the sync instead of retrying
  2. After repeated 404s, reconcile with a fresh sync-token via the WebDAV sync-collection REPORT
  3. When creating a card, PUT it directly rather than GET-then-create
  4. If the card should exist, verify you are addressing the right addressbook URI

Example fix

// before
try {
    $card = $book->getChild('old-uuid.vcard');
} catch (\Exception $e) {
    abortSync();
}

// after
use Sabre\DAV\Exception\NotFound;
try {
    $card = $book->getChild('old-uuid.vcard');
} catch (NotFound $e) {
    $localCache->forget('old-uuid.vcard');
}
Defensive patterns

Strategy: validation

Validate before calling

if (!$addressBook->childExists($name)) {
    return null; // card absent: prune local cache instead of issuing a GET
}

Try / catch

use Sabre\DAV\Exception\NotFound;

try {
    $card = $addressBook->getChild($name);
} catch (NotFound $e) {
    $cache->remove($name);
    return null;
}

Prevention

When it happens

Trigger: GET on a card URI that was never created, was deleted by another client, or was purged after trashbin retention; syncing from a stale local cache that still references removed card URIs and etags.

Common situations: Two clients racing where one deletes while the other fetches; a client database restored from an old backup; the addressbook having been recreated so all URIs changed.

Related errors


AI-assisted analysis of nextcloud/server@ecdeb153ff (2026-08-17). Data as JSON: /api/errors/31a2a7615db3a3ec. Report an issue: GitHub.