CalDAV PROPFIND response contains undeclared C namespace prefix for getctag

Issue Description

When connecting a CalDAV client (OneCalendar) to Stalwart, calendar discovery fails with the XML parsing error ‘C’ is an undeclared prefix.

Investigation shows that when the CalendarServer getctag property is requested via PROPFIND, Stalwart returns the property using the C: namespace prefix, but does not declare that prefix in the XML response.

Authentication, current-user-principal discovery and calendar-home-set discovery succeed normally. The issue occurs when getctag is requested.

Expected Behavior

Stalwart should return well-formed XML and declare the namespace prefix used for the CalendarServer getctag property, for example:

<D:multistatus
xmlns:D=“DAV:”
xmlns:A=“urn:ietf:params:xml:ns:caldav”
xmlns:C=“Calendar and Contacts Server”>

Any namespace prefix would be valid as long as it is correctly bound to Calendar and Contacts Server.

Actual Behavior

Stalwart returns a 207 Multi-Status response whose root element declares only:

<D:multistatus xmlns:D=“DAV:” xmlns:A=“urn:ietf:params:xml:ns:caldav”>

However, the same response later contains:

<C:getctag>"3433"</C:getctag>

No xmlns:C=“Calendar and Contacts Server” declaration is present. The resulting XML is therefore not well-formed, and OneCalendar aborts discovery with ‘C’ is an undeclared prefix.

Reproduction Steps

Configure a Stalwart account with CalDAV enabled.
Send a PROPFIND request with Depth: 0 to the user’s calendar home, requesting the CalendarServer getctag property:

<?xml version="1.0" encoding="UTF-8"?>

<D:propfind xmlns:D=“DAV:”
xmlns:C=“urn:ietf:params:xml:ns:caldav”
xmlns:CS=“Calendar and Contacts Server”>
<D:prop>
<D:resourcetype/>
<D:displayname/>
<C:calendar-description/>
<D:sync-token/>
<D:current-user-privilege-set/>
CS:getctag/
</D:prop>
</D:propfind>
Stalwart responds with HTTP 207 Multi-Status.
Inspect the XML response. <C:getctag> is present, but the C namespace prefix is not declared.
The issue is reproducible directly with curl and therefore does not depend on OneCalendar.

Relevant Log Output

DEBUG WebDAV PROPFIND request (web-dav.propfind) url = “/dav/cal/user%40example.com/”, type = “CalDAV”, details = [“Depth”, 0, “Content-Type”, “text/xml; charset=UTF-8”], result = 207

TRACE HTTP response body (http.response-body) contents = “<?xml version=\"1.0\" encoding=\"UTF-8\"?><D:multistatus xmlns:D="DAV:" xmlns:A="urn:ietf:params:xml:ns:caldav">…<C:getctag>"3433"</C:getctag>…</D:multistatus>”, code = 207

The full trace also confirms that authentication succeeds before the PROPFIND request.

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

Now let’s find where propfind builds the MultiStatus response and calls with_namespace, in the propfind.rs files, to see if GetCTag’s namespace call is missing.

I confirmed this in the source, and it looks like a genuine bug rather than a config issue.

In crates/dav/src/common/propfind.rs, the GetCTag handler correctly calls response.set_namespace(Namespace::CalendarServer) after adding the getctag field, which causes the multistatus root element to declare xmlns:C="http://calendarserver.org/ns/".

However, in crates/dav/src/principal/propfind.rs, which handles PROPFIND requests against calendar/addressbook home collections (the code path hit during calendar discovery), the equivalent WebDavProperty::GetCTag arm builds the <C:getctag> value but never calls set_namespace(Namespace::CalendarServer). As a result the CalendarServer namespace is never registered for the response, so the root <D:multistatus> element is emitted without the xmlns:C declaration even though the body contains a C:getctag element, exactly matching the malformed XML you captured.

This is a server-side bug in the home-collection PROPFIND handler, not something you can work around via configuration. I don’t have a way to file this for you, but a staff or community member will follow up on this thread to confirm and address it.

This is an automated reply from the Stalwart Help Bot. Other community members may follow up if this answer is incomplete or wrong.

Fixed in v0.16.22 (yet to be released).