{"record":{"id":"d86e2f600a978123","repo":"nextcloud/server","slug":"vcards-on-carddav-servers-must-have-a-uid-property","errorCode":null,"errorMessage":"vCards on CardDAV servers MUST have a UID property","messagePattern":"vCards on CardDAV servers MUST have a UID property","errorType":"exception","errorClass":"BadRequest","httpStatus":400,"severity":"error","filePath":"apps/dav/lib/CardDAV/CardDavBackend.php","lineNumber":1580,"sourceCode":"\t\t}\n\t}\n\n\t/**\n\t * Extract UID from vcard\n\t *\n\t * @param string $cardData the vcard raw data\n\t * @return string the uid\n\t * @throws BadRequest if no UID is available or vcard is empty\n\t */\n\tprivate function getUID(string $cardData): string {\n\t\tif ($cardData !== '') {\n\t\t\t$vCard = Reader::read($cardData);\n\t\t\tif ($vCard->UID) {\n\t\t\t\t$uid = $vCard->UID->getValue();\n\t\t\t\treturn $uid;\n\t\t\t}\n\t\t\t// should already be handled, but just in case\n\t\t\tthrow new BadRequest('vCards on CardDAV servers MUST have a UID property');\n\t\t}\n\t\t// should already be handled, but just in case\n\t\tthrow new BadRequest('vCard can not be empty');\n\t}\n\n\t/**\n\t * Mark all cards in an address book as needing to be validated\n\t *\n\t * This is done by setting the modified date to `null`, once a sync runs\n\t * the mtime will be set to a non-null value. Leaving all deleted items with\n\t * a null modified date.\n\t */\n\tpublic function markCardsAsPending(int $addressBookId): void {\n\t\t$query = $this->db->getTypedQueryBuilder();\n\t\t$query->update($this->dbCardsTable)\n\t\t\t->set('lastmodified', $query->createNamedParameter(null))\n\t\t\t->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId)))\n\t\t\t->executeStatement();","sourceCodeStart":1562,"sourceCodeEnd":1598,"githubUrl":"https://github.com/nextcloud/server/blob/ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3/apps/dav/lib/CardDAV/CardDavBackend.php#L1562-L1598","documentation":"CardDavBackend::getUID() parses card data with Sabre\\VObject\\Reader and requires a UID property; without one it throws BadRequest('vCards on CardDAV servers MUST have a UID property') (HTTP 400). RFC 6352 makes UID mandatory for address object resources, and Nextcloud keys card URIs on it, so UID-less cards are rejected on create/update.","triggerScenarios":"PUT of a vCard whose body has no UID: line; importing .vcf files produced by tools that omit UID; hand-built VCARD strings in tests or migration scripts.","commonSituations":"Third-party exporters (CSV converters, CRM dumps) that skip UID; cards truncated in transit so the UID line is cut off; clients that build cards without a UUID generator.","solutions":["Generate a UID (UUID v4) for every card before PUT","Pre-parse imported vcards and inject a 'UID:<uuid>' line where missing","On the 400, patch the card with a UID and re-submit once"],"exampleFix":"// before\nBEGIN:VCARD\nVERSION:4.0\nFN:Ada Lovelace\nEND:VCARD\n-> 400 vCards on CardDAV servers MUST have a UID property\n\n// after\nBEGIN:VCARD\nVERSION:4.0\nUID:8f0f9a5e-6c1e-4a2b-9d3f-7e5c1b2a3d4e\nFN:Ada Lovelace\nEND:VCARD","handlingStrategy":"validation","validationCode":"$vcard = \\Sabre\\VObject\\Reader::read($cardData);\nif (!$vcard->UID) {\n    $vcard->UID = $uuidFactory->uuid4()->toString();\n    $cardData = $vcard->serialize();\n}","typeGuard":"function cardHasUid(string $cardData): bool\n{\n    $v = \\Sabre\\VObject\\Reader::read($cardData);\n    return $v !== null && (bool) $v->UID;\n}","tryCatchPattern":"try {\n    $backend->createCard($bookId, $uri, $cardData);\n} catch (\\Sabre\\DAV\\Exception\\BadRequest $e) {\n    if (str_contains($e->getMessage(), 'UID property')) {\n        $vcard = \\Sabre\\VObject\\Reader::read($cardData);\n        $vcard->UID = $uuidFactory->uuid4()->toString();\n        $backend->createCard($bookId, $uri, $vcard->serialize()); // retry once\n    }\n}","preventionTips":["Always generate a UUID when creating cards client-side","Validate imported .vcf files and inject missing UIDs before upload","UID is the object key on the server: never reuse one across cards"],"tags":["carddav","vcard","http-400","validation","uid"],"backgroundTag":"vcard-missing-uid","analyzedSha":"ecdeb153ffdf227235c9a7e2d13dbe0f9c817bc3","analyzedAt":"2026-08-17T01:36:13.386Z","schemaVersion":2},"datasetVersion":"2026-08-17T04:17:16.089Z"}