Issue Description
When fetching a vCard via a plain GET request on the individual resource, the PHOTO property is returned correctly in valid vCard 3.0 syntax:
PHOTO;TYPE=JPEG;ENCODING=b:/9j/4AAQSkZJRgABAQEAkACQAAD/2wBD…
However, when the same contact is fetched via a CARDDAV:addressbook-multiget REPORT (the mechanism used by CardDAV clients like DAVx5 during normal sync), the PHOTO property comes back malformed:
PHOTO;TYPE=JPEG:data:base64,/9j/4AAQSkZJRgABAQEAkACQAAD/2wBD…
This is neither valid vCard 3.0 nor vCard 4.0 syntax — it mixes the old TYPE=JPEG parameter (v3 style) with a data:base64, URI-style prefix (v4 style), but without a proper MIME type in the data URI (should be data:image/jpeg;base64,… for v4.0) and with a stray escaped comma (,) that shouldn’t be there in either format.
Impact: CardDAV clients (confirmed with DAVx5/ez-vcard) cannot parse this malformed field and silently drop the photo — no error is raised on either the client or server side, making this hard to notice without inspecting raw HTTP traffic.
Expected Behavior
The addressbook-multiget REPORT should return the PHOTO property either unchanged (as the plain GET does), or correctly re-serialized in valid vCard 3.0 or 4.0 syntax — not a mix of both.
Actual Behavior
The PHOTO property comes back malformed:
PHOTO;TYPE=JPEG:data:base64,/9j/4AAQSkZJRgABAQEAkACQAAD/2wBD…
Reproduction Steps
- Store a contact with an embedded JPEG photo (PHOTO;ENCODING=b;TYPE=JPEG:) in a Stalwart address book (in my case, migrated from Nextcloud via vdirsyncer)
- Fetch the resource directly via GET /dav/card///.vcf → PHOTO is correct/valid
- Sync the same address book with a CardDAV client that uses addressbook-multiget (e.g. DAVx5 with verbose logging enabled) → PHOTO in the REPORT response is malformed as shown above
- Contact photo does not appear on the client device, despite no sync errors being reported
Relevant Log Output
DAVx5 verbose log snippet (see last line):
07-27 11:32:09.544 10901 12081 I .davdroid.sync.ContactsSyncManager: Processing CardDAV resource 82ecc683-5b72-472f-962f-0df1f39c254d.vcf
07-27 11:32:09.545 10901 12211 V .davdroid.network.HttpClientBuilder:
07-27 11:32:09.546 10901 12211 V .davdroid.network.HttpClientBuilder: <D:multistatus xmlns:D=“DAV:” xmlns:B=“urn:ietf:params:xml:ns:carddav”><D:response><D:href>/dav/card/uli%40domain.de/kontakte/9222c463-057f-485d-b184-c445d0b9d6fc.vcf</D:href><D:propstat><D:prop><D:getcontenttype>text/vcard</D:getcontenttype><D:getetag>“2715294701”</D:getetag><B:address-data><![CDATA[BEGIN:VCARD
07-27 11:32:09.546 10901 12211 V .davdroid.network.HttpClientBuilder: VERSION:4.0
07-27 11:32:09.546 10901 12211 V .davdroid.network.HttpClientBuilder: N:Lawinenlage;Schweiz;;;
07-27 11:32:09.546 10901 12211 V .davdroid.network.HttpClientBuilder: FN:Lawinenlage, Schweiz
07-27 11:32:09.546 10901 12211 V .davdroid.network.HttpClientBuilder: CLASS:PUBLIC
07-27 11:32:09.546 10901 12211 V .davdroid.network.HttpClientBuilder: PHOTO;TYPE=JPEG:data:base64,/9j/4AAQSkZJRgABAQEAkACQAAD/2wBDAAMCAgICAgMCAgI
Stalwart Version
v0.16.x
Installation Method
Docker
Database Backend
RocksDB
Blob Storage
RocksDB
Search Engine
Internal
Directory Backend
Internal
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