monicahq/monica · error · DavClientException

Could not get address book data.

Error message

Could not get address book data.

What it means

Thrown by Monica's address book subscription service when it cannot obtain address book data from a remote CardDAV server. CreateAddressBookSubscription::execute() validates the account/vault/author input, builds a DavClient from the supplied base_uri/username/password, and asks AddressBookGetter to discover the address book (uri, capabilities, name). If that discovery yields nothing usable, the service aborts before creating the AddressBookSubscription model. Note that in the current code the getter either returns an array or throws first ('No address book found', DavServerNotCompliantException, or a rethrown Guzzle ClientException), so this line acts as the defensive catch-all for a null return.

Source

Thrown at app/Domains/Contact/DavClient/Services/CreateAddressBookSubscription.php:52

            'author_must_belong_to_account',
            'author_must_be_in_vault',
            'author_must_be_vault_manager',
            'vault_must_belong_to_account',
        ];
    }

    /**
     * Add a new Adress Book.
     *
     * @throws DavClientException
     */
    public function execute(array $data): AddressBookSubscription
    {
        $this->validateRules($data);

        $addressBookData = $this->getAddressBookData($data);
        if (! $addressBookData) {
            throw new DavClientException(trans('Could not get address book data.'));
        }

        return $this->createAddressBook($data, $addressBookData);
    }

    private function createAddressBook(array $data, array $addressBookData): AddressBookSubscription
    {
        return AddressBookSubscription::create([
            'user_id' => $this->author->id,
            'vault_id' => $this->vault->id,
            'username' => $data['username'],
            'password' => $data['password'],
            'uri' => $addressBookData['uri'],
            'capabilities' => $addressBookData['capabilities'],
        ]);
    }

    private function getAddressBookData(array $data): ?array

View on GitHub (pinned to e08e917341)

Solutions

  1. Verify endpoint and credentials outside Monica: curl -u user:pass -X PROPFIND -H 'Depth: 0' <base_uri> must return a 207 multi-status with a DAV header
  2. Point base_uri at the documented CardDAV endpoint (e.g. https://cloud.example.com/remote.php/dav for Nextcloud, https://www.googleapis.com/.well-known/carddav for Google) and use an app password where required
  3. Make sure the account on the remote server actually owns at least one address book (create one in the server UI first)
  4. In the calling controller, catch DavClientException and surface it as a 422 validation error instead of letting it bubble up as a 500

Example fix

// before
public function store(Request $request)
{
    $subscription = app(CreateAddressBookSubscription::class)->execute($data); // throws -> 500
}

// after
use App\Domains\Contact\DavClient\Services\Utils\Dav\DavClientException;

public function store(Request $request)
{
    try {
        $subscription = app(CreateAddressBookSubscription::class)->execute($data);
    } catch (DavClientException $e) {
        throw ValidationException::withMessages([
            'base_uri' => $e->getMessage(),
        ]);
    }
}
Defensive patterns

Strategy: try-catch

Validate before calling

// Preflight: confirm the DAV endpoint answers an authenticated PROPFIND before subscribing
use Illuminate\Support\Facades\Http;

$response = Http::withBasicAuth($username, $password)
    ->withHeaders(['Depth' => '0', 'Content-Type' => 'application/xml'])
    ->send('PROPFIND', $baseUri);

if ($response->status() !== 207) {
    throw ValidationException::withMessages([
        'base_uri' => "DAV endpoint did not answer 207 (got {$response->status()}).",
    ]);
}

Try / catch

use App\Domains\Contact\DavClient\Services\Utils\Dav\DavClientException;
use App\Domains\Contact\DavClient\Services\Utils\Dav\DavServerNotCompliantException;

try {
    $subscription = app(CreateAddressBookSubscription::class)->execute($data);
} catch (DavClientException|DavServerNotCompliantException $e) {
    // server answered but no usable address book / not compliant -> user-facing 422
    throw ValidationException::withMessages(['base_uri' => $e->getMessage()]);
} // Guzzle\Exception\ClientException may also propagate: let it bubble or map to 502

Prevention

When it happens

Trigger: Calling the 'add address book subscription' flow with a base_uri/username/password combination for which AddressBookGetter::execute() cannot produce address book data: the DAV endpoint is unreachable, credentials are rejected on every discovery PROPFIND, or the server exposes no address book. Typical concrete calls: POST with base_uri pointing at the server's web UI instead of its CardDAV endpoint, or the account password supplied where the provider requires an app-specific password (Google, Nextcloud).

Common situations: Wrong endpoint URL (web URL vs remote.php/dav or the Google CardDAV endpoint), missing app-specific password, reverse proxy stripping DAV headers so discovery fails, or a remote account that owns no address book yet.

Related errors


AI-assisted analysis of monicahq/monica@e08e917341 (2026-08-17). Data as JSON: /api/errors/8bc3856cf08ac8bd. Report an issue: GitHub.