Skip to content

Home / guides / error-handling

Error Handling#

All exceptions raised by Icechunk derive from IcechunkError. Subclasses group failures into categories by what a handler would do about them, and every instance carries a stable machine-readable kind code identifying the precise failure.

The exception tree#

IcechunkError
├── ConflictError
│   └── RebaseFailedError
├── NotFoundError                 (also a KeyError)
│   ├── NodeNotFoundError
│   ├── SnapshotNotFoundError
│   ├── RefNotFoundError
│   └── RepositoryNotFoundError
├── AlreadyExistsError
├── ReadOnlyError
├── InvalidInputError             (also a ValueError)
├── SessionStateError
├── StorageError
├── FormatError
└── InternalError
Exception Raised when
ConflictError A concurrent writer got there first: commit conflicts, branch update conflicts
RebaseFailedError A rebase could not resolve all conflicts; carries snapshot and conflicts
NotFoundError Something that was asked for does not exist
NodeNotFoundError No group, array, or chunk at the given path
SnapshotNotFoundError No snapshot with the given id (or at the given time)
RefNotFoundError No branch or tag with the given name
RepositoryNotFoundError No Icechunk repository at the given storage location
AlreadyExistsError Something being created already exists (node, branch, tag...)
ReadOnlyError A write through a read-only session, store, or repository
InvalidInputError An argument, key, index, or metadata value is invalid
SessionStateError The operation is not allowed in the current session or repository state
StorageError The object store or local storage failed, or is misconfigured; often transient
FormatError On-disk data could not be read or written: corruption, unsupported spec version
InternalError An unexpected internal error; likely a bug worth reporting

Catching exceptions#

Catch a category when your handler treats every failure in it the same way:

import icechunk

try:
    session.commit("append forecast")
except icechunk.ConflictError:
    session.rebase(icechunk.ConflictDetector())
    session.commit("append forecast")
try:
    session = repo.readonly_session(tag=name)
except icechunk.NotFoundError:
    session = repo.readonly_session(branch="main")

Catch IcechunkError when you only want to know "was this Icechunk?":

try:
    ingest(repo)
except icechunk.IcechunkError as e:
    log.error("ingest failed", kind=e.kind, message=str(e))
    raise

Dispatching on kind#

When a category is too coarse, match the kind attribute — an ErrorKind code that pins down the exact failure without needing one class per failure:

try:
    repo.create_tag(name, snapshot_id=snapshot_id)
except icechunk.AlreadyExistsError as e:
    if e.kind == icechunk.ErrorKind.TAG_PREVIOUSLY_DELETED:
        raise RuntimeError(f"tag {name} was deleted and cannot be recreated") from e
    pass  # already tagged: fine, idempotent

Codes are append-only: new ones may appear in any release, but existing ones are never renamed or removed, so it is safe to persist and compare them.

Compatibility with builtin exceptions#

Two categories also derive from the builtin exception you'd expect, so pre-existing handlers keep working:

  • NotFoundError is a KeyError
  • InvalidInputError is a ValueError
try:
    session = repo.readonly_session(branch="nope")
except KeyError:  # RefNotFoundError is a KeyError
    ...

The KeyError is most relevant for zarr, which internally checks for it when handling missing keys. ValueError is kept for preserving compatibility with earlier Icechunk releases, which raised it for invalid input.

What an error looks like#

str(e) (also available as e.message) is the chain of causes followed by the operation context — which Icechunk operations were running, with their arguments:

>>> try:
...     repo.readonly_session(branch="nope")
... except icechunk.IcechunkError as e:
...     print(e)
ref not found `nope`

context:
   0: icechunk::repository::lookup_branch_v2
           with branch="nope" repo_info=Some(RepoInfo { ... })
             at icechunk/src/repository.rs:1240
   1: icechunk::repository::resolve_ref_version_v2
           with version=BranchTipRef("nope")
             at icechunk/src/repository.rs:1690
   2: icechunk::repository::resolve_version_v2
           with version=BranchTipRef("nope")
             at icechunk/src/repository.rs:1772
   3: icechunk::repository::readonly_session
           with version=BranchTipRef("nope")
             at icechunk/src/repository.rs:1925

repr(e) identifies the class and kind:

>>> repr(e)
'icechunk.RefNotFoundError(message="ref not found `nope`...", kind="ref-not-found")'

Uncaught, the traceback shows the exception as usual, plus the full diagnostic report attached as a PEP 678 note:

Traceback (most recent call last):
  File "ingest.py", line 4, in <module>
    repo.readonly_session(branch="nope")
  File ".../icechunk/repository.py", line 1534, in readonly_session
    self._repository.readonly_session(
icechunk.RefNotFoundError: ref not found `nope`

context:
   0: icechunk::repository::lookup_branch_v2
   ...
  x ref not found `nope`
  |
  | context:
  |    0: icechunk::repository::lookup_branch_v2
  |    ...
  `-> ref not found `nope`

All error kinds#

icechunk.ErrorKind #

Bases: StrEnum

Stable machine-readable codes for the kind attribute of IcechunkError.

Codes are append-only: new ones may appear in any release, but existing ones are not renamed or removed, since user code matches on them (if e.kind == ErrorKind.CHUNK_NOT_FOUND:) and would silently miss a renamed code.

Members are written out literally (rather than generated from the Rust side) so type checkers can see them; test_error_kind_enum_in_sync_with_rust keeps them in sync with the codes Rust actually raises.

ANCESTOR_NODE_NOT_FOUND class-attribute instance-attribute #

ANCESTOR_NODE_NOT_FOUND = 'ancestor-node-not-found'

BAD_KEY_PREFIX class-attribute instance-attribute #

BAD_KEY_PREFIX = 'bad-key-prefix'

BAD_METADATA class-attribute instance-attribute #

BAD_METADATA = 'bad-metadata'

BAD_PREFIX class-attribute instance-attribute #

BAD_PREFIX = 'bad-prefix'

BAD_REDIRECT class-attribute instance-attribute #

BAD_REDIRECT = 'bad-redirect'

BAD_SNAPSHOT_CHAIN_FOR_DIFF class-attribute instance-attribute #

BAD_SNAPSHOT_CHAIN_FOR_DIFF = 'bad-snapshot-chain-for-diff'

BRANCH_ALREADY_EXISTS class-attribute instance-attribute #

BRANCH_ALREADY_EXISTS = 'branch-already-exists'

BRANCH_NOT_FOUND class-attribute instance-attribute #

BRANCH_NOT_FOUND = 'branch-not-found'

BRANCH_UPDATE_CONFLICT class-attribute instance-attribute #

BRANCH_UPDATE_CONFLICT = 'branch-update-conflict'

CANNOT_DELETE_MAIN class-attribute instance-attribute #

CANNOT_DELETE_MAIN = 'cannot-delete-main'

CANNOT_FORK_READ_ONLY_SESSION class-attribute instance-attribute #

CANNOT_FORK_READ_ONLY_SESSION = (
    "cannot-fork-read-only-session"
)

CANNOT_PARSE_URL class-attribute instance-attribute #

CANNOT_PARSE_URL = 'cannot-parse-url'

CHUNK_NOT_FOUND class-attribute instance-attribute #

CHUNK_NOT_FOUND = 'chunk-not-found'

COMMIT_CONFLICT class-attribute instance-attribute #

COMMIT_CONFLICT = 'commit-conflict'

COMMIT_NOT_ALLOWED class-attribute instance-attribute #

COMMIT_NOT_ALLOWED = 'commit-not-allowed'

CONCURRENCY class-attribute instance-attribute #

CONCURRENCY = 'concurrency'

CONFIG_DESERIALIZATION class-attribute instance-attribute #

CONFIG_DESERIALIZATION = 'config-deserialization'

CONFIG_UPDATED class-attribute instance-attribute #

CONFIG_UPDATED = 'config-updated'

CONFLICTING_PATH_NOT_FOUND class-attribute instance-attribute #

CONFLICTING_PATH_NOT_FOUND = 'conflicting-path-not-found'

DESERIALIZATION class-attribute instance-attribute #

DESERIALIZATION = 'deserialization'

DUPLICATE_SNAPSHOT_ID class-attribute instance-attribute #

DUPLICATE_SNAPSHOT_ID = 'duplicate-snapshot-id'

EMPTY_PREFIX_CREATION class-attribute instance-attribute #

EMPTY_PREFIX_CREATION = 'empty-prefix-creation'

FEATURE_FLAG_DISABLED class-attribute instance-attribute #

FEATURE_FLAG_DISABLED = 'feature-flag-disabled'

FLUSH class-attribute instance-attribute #

FLUSH = 'flush'

INCONSISTENT_MANIFESTS class-attribute instance-attribute #

INCONSISTENT_MANIFESTS = 'inconsistent-manifests'

INVALID_ARRAY_METADATA class-attribute instance-attribute #

INVALID_ARRAY_METADATA = 'invalid-array-metadata'

INVALID_BYTE_RANGE class-attribute instance-attribute #

INVALID_BYTE_RANGE = 'invalid-byte-range'

INVALID_CHUNK_INDEX class-attribute instance-attribute #

INVALID_CHUNK_INDEX = 'invalid-chunk-index'

INVALID_COMMIT_CONFIGURATION class-attribute instance-attribute #

INVALID_COMMIT_CONFIGURATION = (
    "invalid-commit-configuration"
)

INVALID_CREDENTIALS class-attribute instance-attribute #

INVALID_CREDENTIALS = 'invalid-credentials'

INVALID_FEATURE_FLAG class-attribute instance-attribute #

INVALID_FEATURE_FLAG = 'invalid-feature-flag'

INVALID_FILE_FORMAT class-attribute instance-attribute #

INVALID_FILE_FORMAT = 'invalid-file-format'

INVALID_KEY class-attribute instance-attribute #

INVALID_KEY = 'invalid-key'

INVALID_MANIFEST_SPLIT_INDEX class-attribute instance-attribute #

INVALID_MANIFEST_SPLIT_INDEX = (
    "invalid-manifest-split-index"
)

INVALID_OBJECT_SIZE class-attribute instance-attribute #

INVALID_OBJECT_SIZE = 'invalid-object-size'

INVALID_PATH class-attribute instance-attribute #

INVALID_PATH = 'invalid-path'

INVALID_REF_NAME class-attribute instance-attribute #

INVALID_REF_NAME = 'invalid-ref-name'

INVALID_REF_TYPE class-attribute instance-attribute #

INVALID_REF_TYPE = 'invalid-ref-type'

INVALID_REPOSITORY_MIGRATION class-attribute instance-attribute #

INVALID_REPOSITORY_MIGRATION = (
    "invalid-repository-migration"
)

INVALID_SNAPSHOT_ID class-attribute instance-attribute #

INVALID_SNAPSHOT_ID = 'invalid-snapshot-id'

INVALID_VIRTUAL_REF class-attribute instance-attribute #

INVALID_VIRTUAL_REF = 'invalid-virtual-ref'

IO class-attribute instance-attribute #

IO = 'io'

KEY_NOT_FOUND class-attribute instance-attribute #

KEY_NOT_FOUND = 'key-not-found'

MANIFEST_INFO_NOT_FOUND class-attribute instance-attribute #

MANIFEST_INFO_NOT_FOUND = 'manifest-info-not-found'

MERGE_FAILED class-attribute instance-attribute #

MERGE_FAILED = 'merge-failed'

MERGE_NOT_ALLOWED class-attribute instance-attribute #

MERGE_NOT_ALLOWED = 'merge-not-allowed'

MOVE_DESTINATION_PARENT_MISSING class-attribute instance-attribute #

MOVE_DESTINATION_PARENT_MISSING = (
    "move-destination-parent-missing"
)

MOVE_DESTINATION_PARENT_NOT_GROUP class-attribute instance-attribute #

MOVE_DESTINATION_PARENT_NOT_GROUP = (
    "move-destination-parent-not-group"
)

MOVE_INTO_SELF_OR_DESCENDANT class-attribute instance-attribute #

MOVE_INTO_SELF_OR_DESCENDANT = (
    "move-into-self-or-descendant"
)

MOVE_WONT_OVERWRITE class-attribute instance-attribute #

MOVE_WONT_OVERWRITE = 'move-wont-overwrite'

NODE_ALREADY_EXISTS class-attribute instance-attribute #

NODE_ALREADY_EXISTS = 'node-already-exists'

NODE_NOT_FOUND class-attribute instance-attribute #

NODE_NOT_FOUND = 'node-not-found'

NON_REARRANGE_SESSION class-attribute instance-attribute #

NON_REARRANGE_SESSION = 'non-rearrange-session'

NOT_ALLOWED class-attribute instance-attribute #

NOT_ALLOWED = 'not-allowed'

NOT_AN_ARRAY class-attribute instance-attribute #

NOT_AN_ARRAY = 'not-an-array'

NOT_A_GROUP class-attribute instance-attribute #

NOT_A_GROUP = 'not-a-group'

NOT_ON_BRANCH class-attribute instance-attribute #

NOT_ON_BRANCH = 'not-on-branch'

NO_AMEND_FOR_INITIAL_COMMIT class-attribute instance-attribute #

NO_AMEND_FOR_INITIAL_COMMIT = 'no-amend-for-initial-commit'

NO_CHANGES_TO_COMMIT class-attribute instance-attribute #

NO_CHANGES_TO_COMMIT = 'no-changes-to-commit'

NO_CONTAINER_FOR_VIRTUAL_CHUNK class-attribute instance-attribute #

NO_CONTAINER_FOR_VIRTUAL_CHUNK = (
    "no-container-for-virtual-chunk"
)

NO_SNAPSHOT class-attribute instance-attribute #

NO_SNAPSHOT = 'no-snapshot'

NO_SNAPSHOT_AT_TIME class-attribute instance-attribute #

NO_SNAPSHOT_AT_TIME = 'no-snapshot-at-time'

OBJECT_NOT_FOUND class-attribute instance-attribute #

OBJECT_NOT_FOUND = 'object-not-found'

OBJECT_STORE class-attribute instance-attribute #

OBJECT_STORE = 'object-store'

PARENT_DIRECTORY_NOT_CLEAN class-attribute instance-attribute #

PARENT_DIRECTORY_NOT_CLEAN = 'parent-directory-not-clean'

PARTIAL_VALUES_PANIC class-attribute instance-attribute #

PARTIAL_VALUES_PANIC = 'partial-values-panic'

PICKLE class-attribute instance-attribute #

PICKLE = 'pickle'

POISON_LOCK class-attribute instance-attribute #

POISON_LOCK = 'poison-lock'

READ_ONLY_REPOSITORY class-attribute instance-attribute #

READ_ONLY_REPOSITORY = 'read-only-repository'

READ_ONLY_SESSION class-attribute instance-attribute #

READ_ONLY_SESSION = 'read-only-session'

READ_ONLY_STORAGE class-attribute instance-attribute #

READ_ONLY_STORAGE = 'read-only-storage'

READ_ONLY_STORE class-attribute instance-attribute #

READ_ONLY_STORE = 'read-only-store'

REARRANGE_SESSION_ONLY class-attribute instance-attribute #

REARRANGE_SESSION_ONLY = 'rearrange-session-only'

REBASE_FAILED class-attribute instance-attribute #

REBASE_FAILED = 'rebase-failed'

REBASE_TX_LOG_PRUNED class-attribute instance-attribute #

REBASE_TX_LOG_PRUNED = 'rebase-tx-log-pruned'

REF_NOT_FOUND class-attribute instance-attribute #

REF_NOT_FOUND = 'ref-not-found'

REPOSITORY_NOT_FOUND class-attribute instance-attribute #

REPOSITORY_NOT_FOUND = 'repository-not-found'

REPO_INFO_UPDATED class-attribute instance-attribute #

REPO_INFO_UPDATED = 'repo-info-updated'

SEMAPHORE_ACQUIRE class-attribute instance-attribute #

SEMAPHORE_ACQUIRE = 'semaphore-acquire'

SERIALIZATION class-attribute instance-attribute #

SERIALIZATION = 'serialization'

SNAPSHOT_NOT_FOUND class-attribute instance-attribute #

SNAPSHOT_NOT_FOUND = 'snapshot-not-found'

SNAPSHOT_TIMESTAMP class-attribute instance-attribute #

SNAPSHOT_TIMESTAMP = 'snapshot-timestamp'

STORAGE_CONFIG class-attribute instance-attribute #

STORAGE_CONFIG = 'storage-config'

STORAGE_OTHER class-attribute instance-attribute #

STORAGE_OTHER = 'storage-other'

TAG_ALREADY_EXISTS class-attribute instance-attribute #

TAG_ALREADY_EXISTS = 'tag-already-exists'

TAG_ERROR class-attribute instance-attribute #

TAG_ERROR = 'tag-error'

TAG_NOT_FOUND class-attribute instance-attribute #

TAG_NOT_FOUND = 'tag-not-found'

TAG_PREVIOUSLY_DELETED class-attribute instance-attribute #

TAG_PREVIOUSLY_DELETED = 'tag-previously-deleted'

UNAUTHORIZED_VIRTUAL_CHUNK_CONTAINER class-attribute instance-attribute #

UNAUTHORIZED_VIRTUAL_CHUNK_CONTAINER = (
    "unauthorized-virtual-chunk-container"
)

UNCOMMITTED_CHANGES class-attribute instance-attribute #

UNCOMMITTED_CHANGES = 'uncommitted-changes'

UNIMPLEMENTED class-attribute instance-attribute #

UNIMPLEMENTED = 'unimplemented'

UNKNOWN class-attribute instance-attribute #

UNKNOWN = 'unknown'

UNSUPPORTED_OPERATION_FOR_VERSION class-attribute instance-attribute #

UNSUPPORTED_OPERATION_FOR_VERSION = (
    "unsupported-operation-for-version"
)

UNSUPPORTED_SPEC_VERSION class-attribute instance-attribute #

UNSUPPORTED_SPEC_VERSION = 'unsupported-spec-version'

UPDATE_ATTEMPTS_EXHAUSTED class-attribute instance-attribute #

UPDATE_ATTEMPTS_EXHAUSTED = 'update-attempts-exhausted'

VIRTUAL_CHUNK_FETCH class-attribute instance-attribute #

VIRTUAL_CHUNK_FETCH = 'virtual-chunk-fetch'

VIRTUAL_CHUNK_MODIFIED class-attribute instance-attribute #

VIRTUAL_CHUNK_MODIFIED = 'virtual-chunk-modified'