Source code for calendaring_sync.error

# SPDX-FileCopyrightText: 2026 calendaring-sync contributors
# SPDX-License-Identifier: AGPL-3.0-or-later

"""calendaring-sync's error hierarchy.

Every error the library raises on purpose derives from
:class:`CalendaringSyncError`, except for arguments that can never be
valid: those raise :class:`ValueError`, such as a feed page size of 0, or
:class:`TypeError`, such as content that isn't bytes. An argument refused
for what the store holds, such as a record of another scope, raises an
error that is both a :class:`CalendaringSyncError` and a
:class:`ValueError`, as a record a store can't hold does with
:class:`RecordValueError`.
"""


[docs] class CalendaringSyncError(Exception): """Base for every calendaring-sync error."""
[docs] class StaleChangeSetError(CalendaringSyncError): """A :term:`change set` was fetched from a token the :term:`scope` no longer has. Another process ingested first, or started another listing of the same scope, so the changes fetched are outdated. Nothing was ingested; the next sync fetches from the scope's current token. """
[docs] class ListingRequiredError(CalendaringSyncError, ValueError): """An incremental :term:`change set` arrived for a :term:`scope` with no :term:`sync token`. A scope gets its token from its first :term:`full listing`, and again after a :term:`reset`, so only a listing can be ingested until then. """
[docs] class UnknownVersionError(CalendaringSyncError, ValueError): """A :term:`full listing` reported an object unchanged, but the store holds no version for it. An adapter reports an object unchanged only when its version equals one :meth:`~calendaring_sync.replica.Replica.known_versions` returned, so the listing was built from versions the store no longer has. Nothing was ingested; build the listing again. """
[docs] class TrimmedFeedError(CalendaringSyncError): """A :term:`cursor` is older than the oldest :term:`change feed` entry kept. The entries after the cursor were trimmed, so they can't be read. Read the scope's records again, and continue from :meth:`~calendaring_sync.replica.Replica.feed_head`. """
[docs] class FeedRangeError(CalendaringSyncError, ValueError): """A :term:`change feed` number is below 0 or past the last entry. No entry has that number yet, so the number didn't come from this store's feed, or the store was replaced since. """
[docs] class UnknownScopeError(CalendaringSyncError): """The :term:`store` has no :term:`scope` with this id."""
[docs] class UnknownRecordError(CalendaringSyncError): """The :term:`store` has no :term:`record` with this :term:`local key`."""
[docs] class DuplicateObjectError(CalendaringSyncError): """Another :term:`record` in the :term:`scope` already has this :term:`object id`."""
[docs] class OtherScopeError(CalendaringSyncError, ValueError): """A :term:`record` or :term:`change set` belongs to another :term:`scope`. A session or adapter works on one scope, and refuses what belongs to another before anything is written. """
[docs] class ObjectIdAssignedError(CalendaringSyncError, ValueError): """The :term:`record` already has an :term:`object id`, so it can't be given another. Only a create that has none takes an object id when its push is claimed; claim any other record without one. """
[docs] class LoneSurrogateError(CalendaringSyncError, ValueError): """A string holds a :ref:`lone surrogate <lone-surrogates>`, which no store can keep. It's refused where the string comes in, before anything is written. """
[docs] class RecordValueError(CalendaringSyncError, ValueError): """A :term:`record` holds what its state can't, so a :term:`store` refuses to write it. For example a pending record without a :term:`pending edit`, or a :term:`conflict` that says the server removed the object but holds the server's content. It's a :class:`ValueError` too, since such a record can never be valid. """
[docs] class EditRefusedError(CalendaringSyncError): """The :term:`record` is in a state that takes no :term:`local edit`. Its subclasses say which: a :term:`push` :term:`in flight`, an open :term:`conflict`, or a record the server removed. """
[docs] class PushInFlightError(EditRefusedError): """The :term:`record`'s :term:`push` is :term:`in flight`; edit it once the push has ended."""
[docs] class OpenConflictError(EditRefusedError): """The :term:`record` holds an open :term:`conflict`; resolve it before editing."""
[docs] class RemovedRecordError(EditRefusedError): """The :term:`record` was removed from the server, so there's nothing to edit."""
[docs] class StaleConflictError(CalendaringSyncError): """The :term:`conflict` changed since your application read it, so a resolution isn't applied. The record ended, or its edit or the server's copy moved on. Read it again and decide on what it holds now. """
[docs] class UnknownHolderError(CalendaringSyncError): """No single record is known to hold what a refused create was refused for. The server named no record that's still there, and none or several hold the create's :term:`logical key`, or it was sent without one. So the create can't be pushed as a change to one of them. Resolve it another way, or sync first. """
[docs] class PendingEditError(CalendaringSyncError): """The :term:`record` holds a :term:`pending edit` of its own, which this would replace. Push or discard that edit first, then try again. """
[docs] class ScopeMismatchError(CalendaringSyncError): """A :term:`session` was opened with a media type the :term:`scope` doesn't have. The stored content is in the scope's media type, so it can't be reused. Drop the scope and sync it again. """
[docs] class ScopeGoneError(CalendaringSyncError): """The server no longer has the :term:`scope`, such as a calendar that was deleted. Nothing was ingested, so the :term:`store` is as it was. A server that lost the scope only for a moment has it back at the next sync. If it's gone for good, :meth:`~calendaring_sync.replica.Replica.drop_scope` returns the pending edits and open conflicts it discards. """
[docs] class IncompleteListingError(CalendaringSyncError): """The server cut a :term:`full listing` short, so it wasn't ingested. A listing that leaves objects out can't tell them from objects the server removed, so the :term:`store` is left as it was. Raising the server's limit on how many results it lists lets the next listing through. """
[docs] class SessionError(CalendaringSyncError): """A :term:`session` was used out of order. For example a second session on a scope that already has one open, an ingest after the session's last page, or a reset while a session is open. """
[docs] class TransactionError(CalendaringSyncError): """A :term:`store` transaction was used the wrong way. Raised for a transaction opened while the thread already has one, and for closing the store inside a transaction. Its subclasses cover an operation outside a transaction and a write inside a read. """
[docs] class TransactionRequiredError(TransactionError): """A :term:`store` operation ran outside a transaction."""
[docs] class TransactionPermissionError(TransactionError): """A :term:`store` operation that writes ran in a transaction opened with ``write=False``."""
[docs] class StoreError(CalendaringSyncError): """The :term:`store` couldn't read or write its data, such as a file that isn't a store."""
[docs] class StoreBusyError(StoreError): """Another process kept the :term:`store` busy for longer than the store waits. Nothing was changed. Try again later. """
[docs] class StoreVersionError(StoreError): """The store was written by a newer calendaring-sync, with a schema this one can't read."""
[docs] class UnexpectedStoreError(CalendaringSyncError): """A :term:`store` failed in a way it doesn't expect, which is a bug in it or its caller. It isn't a :class:`StoreError`, so code that retries on a store error doesn't retry a bug. The original exception is its ``__cause__``. """