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
- Treat the 404 as 'card gone': remove it from the local cache and continue the sync instead of retrying
- After repeated 404s, reconcile with a fresh sync-token via the WebDAV sync-collection REPORT
- When creating a card, PUT it directly rather than GET-then-create
- 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
- Use childExists()/PROPFIND before GETting cached card URIs
- Reconcile state with sync-token REPORTs instead of blind GETs
- On 404, delete local state rather than retrying the fetch
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
- Card not found
- URI too long. Address book not created
- Unknown property: {property}
- Too many addressbooks created
- AddressBook limit reached
AI-assisted analysis of nextcloud/server@ecdeb153ff (2026-08-17).
Data as JSON: /api/errors/31a2a7615db3a3ec.
Report an issue: GitHub.