CardDAV returns vCard 4.0 by default, which makes Apple Contacts sync nothing

Issue Description

Apple Contacts syncs nothing against Stalwart and shows an empty address book,
with no error on either side. The cause is the default vCard version.

Apple’s CardDAV client asks for address-data without naming a version.
Stalwart then serialises vCard 4.0, which Apple Contacts cannot store. It
downloads every card, discards all of them, and reports the sync as finished.

What made this hard to find is that everything else is correct. I went through
principal discovery, addressbook-home-set, resourcetype, getctag, sync-token,
the initial sync-collection REPORT and addressbook-multiget, and Stalwart
answered properly every time. I was convinced the fault was on the client until
I read the actual traffic through a local proxy and saw the VERSION line.

Expected Behavior

When a client requests address-data without a version attribute, I would expect
vCard 3.0, or at least a way to configure the default. RFC 6352 leaves the
choice to the server, and 3.0 appears to be what other implementations settle
on precisely because Apple clients cannot handle 4.0.

Actual Behavior

address-data comes back as VERSION:4.0 whenever the client names no version.
Every Apple client therefore syncs nothing, silently. DAVx5 on Android and
iPadOS are unaffected because they request a version explicitly.

Reproduction Steps

  1. Create an account with an address book containing a few contacts.

  2. Ask for a card without naming a version:

    curl -u USER:PASS -X REPORT -H ‘Depth: 1’ -H ‘Content-Type: application/xml’
    -d ‘<?xml version="1.0"?><c:addressbook-multiget xmlns:d=“DAV:”
    xmlns:c=“urn:ietf:params:xml:ns:carddav”><d:prop><d:getetag/>
    <c:address-data/></d:prop><d:href>HREF</d:href></c:addressbook-multiget>’
    https://HOST/dav/card/USER/default/ | grep -o ‘VERSION:[0-9.]*’

    → VERSION:4.0

  3. Repeat with <c:address-data content-type=“text/vcard” version=“3.0”/>

    → VERSION:3.0

  4. Add the address book to Apple Contacts on macOS. All cards transfer, none
    appear, and the sync is reported as complete.

Relevant Log Output

None, and that is part of the problem. Every request succeeds, so the server
log shows nothing but ordinary traffic:

INFO Authentication successful (auth.success) listenerId = “http”,
localPort = 8080, accountName = “…”, details = “Authenticated with app password”

On the client side macOS logs “Sync of source complete” and exits 0. Neither
end reports anything wrong.

Stalwart Version

v0.16.x

Installation Method

Docker

Database Backend

RocksDB

Blob Storage

RocksDB

Search Engine

Internal

Directory Backend

Internal

Additional Context

supported-address-data advertises 4.0, 3.0 and 2.1, in that order.

My workaround is a small local proxy on the Mac that inserts
version=“3.0” into every address-data element that doesn’t already have one.
That fixes it completely, which is what confirms the diagnosis.

Happy to test a patch.

I have reviewed the documentation and FAQ and confirm that my issue is NOT addressed there.

on

I have searched this support forum (open and closed topics) and confirm this is not a duplicate.

on

I understand that topics in this category are triaged by a bot first but a human reply will follow up. If I’d prefer a human-only reply, I’ll add the no-ai tag to my topic.

on

There is a setting to change the default to 3.0.

Thanks - I found it under Calendar & Contacts → Address Book → vCard Version
and set it to vCard 3.0. The UI keeps the value after saving and after a
redeploy of the container.

The server still answers with 4.0 though. Same request as before, with no
version named:

<c:address-data/> → VERSION:4.0
<c:address-data content-type=“text/vcard” version=“3.0”/> → VERSION:3.0

So the explicit request still works, only the new default does not seem to
apply. Two questions: does anything else have to be reloaded for it to take
effect, and does the default perhaps only apply to cards stored after the
change? All of mine were written before I changed the setting, and they were
stored while the server was still on 4.0.

Version 0.16, Docker image, RocksDB store.

Management → Actions → Reload → Server Settings or restart the node.

Thanks! I restarted the whole service, which didn’t seem to work. This did.