Skip to content

Issue 133 CardDAV Source Could Not Be Saved

Ed Mozley edited this page Sep 16, 2026 · 1 revision

Adding a CardDAV address book failed on Save (issue #133)

Reported by mbsouth Β· Fixed in #1722 and #1723


1. What you saw

System β†’ Authentication β†’ Add provider, choose CardDAV, fill in the server and sign-in details, and Test - which works:

Connected, and found one address book.

Then Save, and an error. The browser console showed the response the page had tried to read:

<br />
<b>Warning</b>:  Undefined array key "carddav_scope" in .../api/system/save_sso_provider.php on line 154
{"success":false,"error":"SQLSTATE[23000]: Integrity constraint violation: 1048 Column 'carddav_scope' cannot be null"}

Nothing was saved. Nobody could add a CardDAV address book, on any installation from 1.8.0 onwards.


2. What was actually wrong

A CardDAV source is set up in two steps. The dialog creates it. Then the settings icon on its row opens a page where you choose which address book to read and which contacts to bring in - everyone, or only certain groups. That second part is stored as the scope.

The save code checked the scope like this:

'scope' => in_array(($data['carddav_scope'] ?? 'all'), ['all', 'group', 'category'], true)
         ? $data['carddav_scope'] : 'all',

Read the two halves separately. The test says "if no scope was sent, treat it as all" - and all is valid, so the test passes. The answer then returns $data['carddav_scope'] - the value that was never sent. That is:

  1. a PHP warning, printed before the JSON, so the page could not read the reply at all, and
  2. an empty value written to a column that does not allow one, so the database refused the row.

Why it was not caught. For twelve minutes on 12 September the scope picker lived in the dialog itself, and the dialog sent a scope with every save. It then moved to the address book's own page - where it belongs, because you cannot pick groups before you have picked a book - and the dialog stopped sending it. The line above was written for the first arrangement and never revisited for the second. The address book page always sends a scope, so saving from there worked; only a brand-new source, created from the dialog, hit the empty value - and no check covered that screen after the move.

A second problem in the same place

Fixing the crash on its own would have exposed a quieter one. Three screens save a provider through this code, and none of them sends every setting:

Screen Did not send
the Add / Edit dialog a CardDAV source's address book, scope and write-back; an LDAP directory's sync settings
the address book page the company it belongs to
the LDAP settings page require verified email and default modules

The save code treated "not sent" as "empty". So renaming an address book in the dialog would have cleared the chosen address book, gone back to importing everyone, and switched write-back off. Renaming an LDAP directory there switched directory sync off. And saving either settings page quietly moved the source back to Global from whichever company it belonged to. The address book page also always switched a source back on, even one you had turned off.


3. How it was fixed

  • The scope is read once, and the default is what gets stored when nothing was sent.
  • When a provider is updated, a setting the request does not mention keeps the value it already had. This applies only while the provider stays the same type - changing an LDAP directory into an address book still clears the LDAP settings, so nothing from the old type is left behind and quietly in force.
  • The address book page no longer sends enabled, so it cannot turn a disabled source back on.

4. Files changed

File Change
api/system/save_sso_provider.php the scope fix; stored values kept for anything not sent
system/sso/carddav.php stopped sending enabled

5. How it was proved

Through the real save endpoint, with exactly what each screen sends:

  • Before the fix, the dialog's payload reproduced the reported warning and database error word for word.
  • After, 13 checks pass: adding from the dialog; choosing a book, groups and write-back on the address book page without losing the company; renaming and disabling in the dialog without losing the book; saving the page again without turning the source back on; the same round trip for an LDAP directory without losing sync, default modules or verified email; switching a provider's type still clearing the old settings; and an update to a provider that does not exist being refused.

FreeITSM

Getting Started

Modules

Multi-tenancy (planned)

Blue sky thinking

Bugs resolved

Links

Clone this wiki locally