Handle errors#

All calendaring-jmap errors extend JMAPBaseError, and the JMAP-specific ones extend JMAPError, which adds an error_type string. See Design for why the hierarchy is shaped this way.

Catch specific error types#

from calendaring_jmap.error import (
    JMAPAuthError,
    JMAPCapabilityError,
    JMAPError,
    JMAPMethodError,
)

try:
    event_id, old_state, new_state = client.create_event(calendar_id, event)
except JMAPAuthError:
    print("Authentication failed (401/403)")
except JMAPCapabilityError:
    print("Server does not support urn:ietf:params:jmap:calendars")
except JMAPMethodError as e:
    print(f"Server rejected the request: {e.error_type}, {e.reason}")
except JMAPError as e:
    print(f"Protocol error: {e}")

See Errors for what each error class means and the full list of JMAP method error types.

Malformed iCalendar input#

create_event, send_invite, and update_event take a JSCalendar dict directly and raise nothing client-side on a malformed one; whether a missing required property comes back as the server’s own invalidProperties (surfaced as JMAPMethodError) depends on the server and the property. Confirmed live that Cyrus rejects a create missing uid/start with invalidProperties, while Stalwart accepts it, generating its own uid and leaving the event with no start at all rather than rejecting the request. The ValueError case below applies to ical_to_jscal itself, and so to JMAPCalendar.add_event/JMAPCalendarObject.save, which call it on your behalf: if the iCalendar string is missing a mandatory property (UID, DTSTART) or has a negative duration, conversion raises a plain ValueError before any request reaches the server. Catch it if you’re constructing iCalendar strings by hand rather than passing through data a server already returned to you:

try:
    event_id = cal.add_event(ical)
except ValueError as e:
    print(f"Malformed iCalendar: {e}")
except JMAPMethodError as e:
    print(f"Server rejected the request: {e.error_type}, {e.reason}")

Last updated on 2026-10-24.