Source code for coinbase.deribit.errors

import re
from typing import Optional

# Deribit JSON-RPC error codes we special-case. Everything else falls back to the
# base DeribitRPCError carrying the raw code + message. The SDK never retries an
# RPC error on the caller's behalf; retry policy belongs to the caller.
RATE_LIMIT_CODE = 10028
MATCHING_QUEUE_FULL_CODE = 10047
INSUFFICIENT_FUNDS_CODE = 10009
INVALID_PARAMS_CODE = 11030
JSONRPC_INVALID_PARAMS_CODE = -32602  # what the gateway returns for bad params

# Codes the gateway returns when an access token is rejected before the request
# runs. The REST client refreshes the token and retries once on these.
TOKEN_REJECTED_CODES = frozenset({13004, 13009})  # invalid_credentials, unauthorized

# JWT-shaped strings (three base64url segments). Gateway error text is copied into
# exceptions and logs; if it ever echoes a credential, this keeps it out.
_JWT_LIKE = re.compile(r"[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}\.[A-Za-z0-9_-]{8,}")


def redact_secrets(text) -> str:
    """Replace JWT-like substrings with ``<redacted>``.

    :meta private:
    """
    return _JWT_LIKE.sub("<redacted>", str(text))


[docs] class DeribitRPCError(Exception): """A JSON-RPC ``error`` object returned by the Deribit gateway.""" def __init__(self, code: int, message: str, data: Optional[dict] = None) -> None: self.code = code self.message = message self.data = data super().__init__(f"[{code}] {message}")
[docs] class DeribitRateLimitError(DeribitRPCError): """Code 10028 — too many requests.""" def __init__(self, code: int, message: str, data: Optional[dict] = None) -> None: super().__init__(code, message, data) retry_after = None if isinstance(data, dict): # Deribit surfaces a cool-down hint here when present. retry_after = data.get("retry_after") or data.get("retry_after_ms") self.retry_after = retry_after
[docs] class DeribitMatchingQueueFullError(DeribitRPCError): """Code 10047 — matching engine queue is full; safe to retry with backoff."""
[docs] class DeribitInsufficientFundsError(DeribitRPCError): """Code 10009 (and variants) — not enough funds."""
[docs] class DeribitInvalidParamsError(DeribitRPCError): """Code 11030 or JSON-RPC -32602 — invalid arguments."""
_CODE_TO_CLASS = { RATE_LIMIT_CODE: DeribitRateLimitError, MATCHING_QUEUE_FULL_CODE: DeribitMatchingQueueFullError, INSUFFICIENT_FUNDS_CODE: DeribitInsufficientFundsError, INVALID_PARAMS_CODE: DeribitInvalidParamsError, JSONRPC_INVALID_PARAMS_CODE: DeribitInvalidParamsError, } def rpc_error_from(error: dict) -> DeribitRPCError: """Build the most specific DeribitRPCError subclass for a JSON-RPC error object. :meta private: """ code = error.get("code", 0) message = error.get("message", "") data = error.get("data") cls = _CODE_TO_CLASS.get(code, DeribitRPCError) return cls(code, message, data)
[docs] class DeribitAuthError(Exception): """Auth pipeline failure (token exchange/refresh) — not a Deribit RPC error."""
[docs] class DeribitConnectionError(Exception): """WS disconnect while a call was pending. Writes are never auto-resent."""
[docs] class DeribitSubscriptionError(Exception): """The gateway did not subscribe some of the requested channels. The gateway answers a subscribe with the channels it accepted and silently drops the rest. Market-data channels are only served on the streams host (an unauthenticated :class:`DeribitRetailWSClient`); ``user.*`` channels only on the trading host (an authenticated one). Accepted channels stay subscribed. - **rejected** - channels that were not subscribed. - **accepted** - channels that were subscribed. """ def __init__(self, rejected, accepted, hint: str = "") -> None: self.rejected = list(rejected) self.accepted = list(accepted) message = f"gateway did not subscribe: {', '.join(self.rejected)}" super().__init__(f"{message}. {hint}".strip() if hint else message)