nextcloud/server · warning · Exception

This addressbook is immutable

Error message

This addressbook is immutable

What it means

Generic PHP Exception thrown by the contactsinteraction 'Recently contacted' address book (an ExternalAddressBook exposed at .../contactsinteraction_recent) from delete(), createFile() and propPatch(). This address book is a read-only, auto-generated view over the recent-contacts table, so every mutating CardDAV operation is deliberately refused; getProperties() even advertises {owncloud}read-only = true.

Source

Thrown at apps/contactsinteraction/lib/AddressBook.php:44

	use ACLTrait;

	public const URI = 'recent';

	public function __construct(
		private RecentContactMapper $mapper,
		private IL10N $l10n,
		private string $principalUri,
	) {
		parent::__construct(Application::APP_ID, self::URI);
	}

	/**
	 * @inheritDoc
	 * @throws Exception
	 */
	#[\Override]
	public function delete(): void {
		throw new Exception('This addressbook is immutable');
	}

	/**
	 * @inheritDoc
	 * @throws Exception
	 */
	#[\Override]
	public function createFile($name, $data = null) {
		throw new Exception('This addressbook is immutable');
	}

	/**
	 * @inheritDoc
	 * @throws NotFound
	 */
	#[\Override]
	public function getChild($name): Card {
		try {

View on GitHub (pinned to ecdeb153ff)

Solutions

  1. In CardDAV clients, honor the {http://owncloud.org/ns}read-only property and exclude the book from writes/sync
  2. Direct users to create contacts in a regular (writable) address book — recently-contacted entries are managed by the server
  3. If the error recurs from a sync client, report it as a client bug (writing to an advertised read-only collection)
  4. Do not attempt server-side cleanup by deleting this address book; the table is pruned by the contactsinteraction background job
Defensive patterns

Strategy: type-guard

Type guard

// CardDAV client: check the read-only flag before any write
function isReadOnlyAddressBook(\Sabre\CardDAV\AddressBook $book): bool {
	$props = $book->getProperties(['{http://owncloud.org/ns}read-only']);
	return (($props['{http://owncloud.org/ns}read-only'] ?? false) === true)
		|| (($props['{http://owncloud.org/ns}read-only'] ?? 0) === 1);
}

Try / catch

try {
	$book->createFile($name, $cardData);
} catch (\Exception $e) {
	if (str_contains($e->getMessage(), 'immutable')) {
		// read-only 'Recently contacted' book — route the write to a user address book instead
	} else {
		throw $e;
	}
}

Prevention

When it happens

Trigger: A CardDAV client issuing DELETE on the address book collection, PUT/POST to create a card inside it, or PROPPATCH on its properties (rename, color) — i.e. any write attempt on the 'recent' address book. Reads (getChildren, getChild, getProperties) succeed; only mutations throw.

Common situations: Sync clients (DAVx5, iOS/macOS Contacts) that try to write cache metadata or reconcile deletions into every synced book; users attempting to delete 'Recently contacted' from the Contacts app; clients that ignore the read-only property and attempt renames.

Related errors


AI-assisted analysis of nextcloud/server@ecdeb153ff (2026-08-17). Data as JSON: /api/errors/d4f27f5450713ff1. Report an issue: GitHub.