File size: 4,708 Bytes
2cf7040 b2101ae 2cf7040 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 | """Typed exception hierarchy for sibyl-memory-client.
Every error has a stable `code` for programmatic handling and a `recovery`
string suggesting what the caller should try next.
v0.4.0 (2026-05-18): `CapExceededError` + `TierVerificationError` relocated
here from `_capcheck.py` so they are importable from the canonical
`sibyl_memory_client.exceptions` submodule path (KAPPA bug report against
sibyl-memory-mcp 0.1.1: server imported these from `.exceptions` but they
only lived on `._capcheck`).
"""
from __future__ import annotations
# Default upgrade URL for cap / tier-related errors. Kept here as a string
# literal so the exceptions module has no dependency on _capcheck (avoids the
# circular import that motivated the v0.4.0 reorganization).
_DEFAULT_UPGRADE_URL = "https://docs.sibyllabs.org/memory/tiers"
class SibylMemoryError(Exception):
"""Base for all sibyl-memory-client errors."""
code: str = "SIBYL_MEMORY_ERROR"
recovery: str = "See exception message for details."
def __init__(self, message: str, *, recovery: str | None = None) -> None:
super().__init__(message)
if recovery is not None:
self.recovery = recovery
class StorageError(SibylMemoryError):
code = "STORAGE_ERROR"
recovery = "Check disk space and file permissions on ~/.sibyl-memory/."
class SchemaError(SibylMemoryError):
code = "SCHEMA_ERROR"
recovery = "The schema file is missing or corrupt. Re-install sibyl-memory-client."
class TenantError(SibylMemoryError):
code = "TENANT_ERROR"
recovery = "Set a tenant before calling write/read operations: client.set_tenant(uuid)."
class NotFoundError(SibylMemoryError):
code = "NOT_FOUND"
recovery = "The requested entity / state / reference does not exist."
class ConflictError(SibylMemoryError):
code = "CONFLICT"
recovery = "An entity with this (tenant_id, category, name) already exists. Use update_entity() instead."
class ValidationError(SibylMemoryError):
code = "VALIDATION_ERROR"
recovery = "Body must be a JSON-serializable dict / list / primitive."
class TierGateError(SibylMemoryError):
"""Raised when a free-tier user invokes a paid-tier-only feature.
Carries the user's current tier + an upgrade URL so callers can render
a clean prompt. Self-learning + memory linter are both gated by this
on the free tier; upgrading to any paid tier unlocks both.
"""
code = "TIER_GATE"
recovery = (
"Upgrade your plugin tier to unlock this feature. See "
"https://sibyllabs.org/plugin#tier for options "
"(Sibyl Stake / Sync / Lifetime / Enterprise)."
)
def __init__(
self,
message: str,
*,
feature: str,
current_tier: str = "free",
upgrade_url: str = "https://sibyllabs.org/plugin#tier",
) -> None:
super().__init__(message)
self.feature = feature
self.current_tier = current_tier
self.upgrade_url = upgrade_url
class CapExceededError(SibylMemoryError):
"""Raised when a free-tier user tries to write past the 2 MB cap.
Carries the upgrade URL so callers (CLIs, IDEs, agent frameworks) can
render a clean upgrade prompt.
v0.4.0: moved from `_capcheck.py` to `exceptions.py` so the canonical
`sibyl_memory_client.exceptions` submodule path exports it. The class
contract (code, recovery, current_size, cap, proposed_delta, upgrade_url
attributes) is unchanged.
"""
code = "CAP_EXCEEDED"
recovery = (
"Upgrade to remove the 2 MB cap. See "
"https://docs.sibyllabs.org/memory/tiers for options "
"(Sibyl Stake / Sync / Lifetime / Enterprise)."
)
def __init__(
self,
message: str,
*,
current_size: int,
cap: int,
proposed_delta: int = 0,
upgrade_url: str = _DEFAULT_UPGRADE_URL,
) -> None:
super().__init__(message)
self.current_size = current_size
self.cap = cap
self.proposed_delta = proposed_delta
self.upgrade_url = upgrade_url
class TierVerificationError(SibylMemoryError):
"""Raised when the SDK can't verify the user's tier and has no cached
grace period to fall back on (offline at the cap with no recent
successful check).
v0.4.0: moved from `_capcheck.py` to `exceptions.py` so the canonical
`sibyl_memory_client.exceptions` submodule path exports it. The class
contract (code, recovery) is unchanged.
"""
code = "TIER_VERIFY_FAILED"
recovery = (
"Connect to the internet so the SDK can verify your account, or "
"stay under the 2 MB free-tier cap until you're online."
)
|