nextcloud/server · error · BadRequest
vCards on CardDAV servers MUST have a UID property
Error message
vCards on CardDAV servers MUST have a UID property
What it means
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.
Source
Thrown at apps/dav/lib/CardDAV/CardDavBackend.php:1580
}
}
/**
* Extract UID from vcard
*
* @param string $cardData the vcard raw data
* @return string the uid
* @throws BadRequest if no UID is available or vcard is empty
*/
private function getUID(string $cardData): string {
if ($cardData !== '') {
$vCard = Reader::read($cardData);
if ($vCard->UID) {
$uid = $vCard->UID->getValue();
return $uid;
}
// should already be handled, but just in case
throw new BadRequest('vCards on CardDAV servers MUST have a UID property');
}
// should already be handled, but just in case
throw new BadRequest('vCard can not be empty');
}
/**
* Mark all cards in an address book as needing to be validated
*
* This is done by setting the modified date to `null`, once a sync runs
* the mtime will be set to a non-null value. Leaving all deleted items with
* a null modified date.
*/
public function markCardsAsPending(int $addressBookId): void {
$query = $this->db->getTypedQueryBuilder();
$query->update($this->dbCardsTable)
->set('lastmodified', $query->createNamedParameter(null))
->where($query->expr()->eq('addressbookid', $query->createNamedParameter($addressBookId)))
->executeStatement();View on GitHub (pinned to ecdeb153ff)
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
Example fix
// before BEGIN:VCARD VERSION:4.0 FN:Ada Lovelace END:VCARD -> 400 vCards on CardDAV servers MUST have a UID property // after BEGIN:VCARD VERSION:4.0 UID:8f0f9a5e-6c1e-4a2b-9d3f-7e5c1b2a3d4e FN:Ada Lovelace END:VCARD
Defensive patterns
Strategy: validation
Validate before calling
$vcard = \Sabre\VObject\Reader::read($cardData);
if (!$vcard->UID) {
$vcard->UID = $uuidFactory->uuid4()->toString();
$cardData = $vcard->serialize();
} Type guard
function cardHasUid(string $cardData): bool
{
$v = \Sabre\VObject\Reader::read($cardData);
return $v !== null && (bool) $v->UID;
} Try / catch
try {
$backend->createCard($bookId, $uri, $cardData);
} catch (\Sabre\DAV\Exception\BadRequest $e) {
if (str_contains($e->getMessage(), 'UID property')) {
$vcard = \Sabre\VObject\Reader::read($cardData);
$vcard->UID = $uuidFactory->uuid4()->toString();
$backend->createCard($bookId, $uri, $vcard->serialize()); // retry once
}
} Prevention
- 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
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- vCard can not be empty
- URI too long. Address book not created
- Unknown property: {property}
- VCard object exceeds $cardSizeLimit bytes
- Message exceeds allowed character limit of 1000
AI-assisted analysis of nextcloud/server@ecdeb153ff (2026-08-17).
Data as JSON: /api/errors/d86e2f600a978123.
Report an issue: GitHub.