ionq_core.exceptions
Structured exceptions for the IonQ API client.
All exceptions inherit from IonQError. The hierarchy is:
IonQError
+-- APIConnectionError # network / DNS failures
| +-- APITimeoutError # request timed out
+-- APIError # HTTP 4xx / 5xx responses
| +-- BadRequestError # 400
| +-- AuthenticationError # 401
| +-- PermissionDeniedError # 403
| +-- NotFoundError # 404
| +-- RateLimitError # 429 (includes retry_after)
| +-- ServerError # 5xx
Example:
from ionq_core import IonQClient, RateLimitError, AuthenticationError client = IonQClient() try: job = create_job.sync(client=client, body=payload) except AuthenticationError: print("Invalid API key") except RateLimitError as e: print(f"Rate limited, retry after {e.retry_after}s")
1# SPDX-FileCopyrightText: 2026 IonQ, Inc. 2# SPDX-License-Identifier: Apache-2.0 3 4"""Structured exceptions for the IonQ API client. 5 6All exceptions inherit from `IonQError`. The hierarchy is: 7 8``` 9IonQError 10+-- APIConnectionError # network / DNS failures 11| +-- APITimeoutError # request timed out 12+-- APIError # HTTP 4xx / 5xx responses 13| +-- BadRequestError # 400 14| +-- AuthenticationError # 401 15| +-- PermissionDeniedError # 403 16| +-- NotFoundError # 404 17| +-- RateLimitError # 429 (includes retry_after) 18| +-- ServerError # 5xx 19``` 20 21Example: 22 ```python 23 from ionq_core import IonQClient, RateLimitError, AuthenticationError 24 25 client = IonQClient() 26 try: 27 job = create_job.sync(client=client, body=payload) 28 except AuthenticationError: 29 print("Invalid API key") 30 except RateLimitError as e: 31 print(f"Rate limited, retry after {e.retry_after}s") 32 ``` 33""" 34 35__all__ = [ 36 "APIConnectionError", 37 "APIError", 38 "APITimeoutError", 39 "AuthenticationError", 40 "BadRequestError", 41 "IonQError", 42 "NotFoundError", 43 "PermissionDeniedError", 44 "RateLimitError", 45 "ServerError", 46] 47 48 49class IonQError(Exception): 50 """Base exception for all IonQ errors. 51 52 Catch this to handle any error raised by the library, including connection 53 failures, API errors, polling timeouts, and job failures. 54 """ 55 56 57class APIConnectionError(IonQError): 58 """Raised when a connection to the IonQ API cannot be established. 59 60 This covers DNS resolution failures, refused connections, and other 61 network-level errors. The original ``httpx`` exception is chained 62 via ``__cause__``. 63 """ 64 65 66class APITimeoutError(APIConnectionError): 67 """Raised when a request to the IonQ API times out. 68 69 Inherits from `APIConnectionError` so that catching connection errors 70 also catches timeouts. 71 """ 72 73 74class APIError(IonQError): 75 """Raised when the IonQ API returns an HTTP error response (4xx or 5xx). 76 77 Attributes: 78 status_code: The HTTP status code. 79 body: The parsed response body (``dict`` if JSON, ``str`` otherwise, 80 or ``None`` if the body could not be read). 81 message: A human-readable error message extracted from the response, 82 or a default ``"HTTP <status>"`` string. 83 request_id: The ``x-request-id`` header from the response, useful for 84 contacting IonQ support about a specific request. 85 """ 86 87 def __init__( 88 self, 89 status_code: int, 90 body: dict | str | None = None, 91 message: str | None = None, 92 *, 93 request_id: str | None = None, 94 ) -> None: 95 self.status_code = status_code 96 self.body = body 97 self.request_id = request_id 98 self.message = message or f"HTTP {status_code}" 99 super().__init__(self.message) 100 101 102class AuthenticationError(APIError): 103 """Raised on ``401 Unauthorized``. 104 105 Typically means the API key is missing, invalid, or revoked. 106 """ 107 108 109class PermissionDeniedError(APIError): 110 """Raised on ``403 Forbidden``. 111 112 The API key is valid but lacks permission for the requested operation. 113 """ 114 115 116class NotFoundError(APIError): 117 """Raised on ``404 Not Found``. 118 119 The requested resource (job, session, backend, etc.) does not exist. 120 """ 121 122 123class BadRequestError(APIError): 124 """Raised on ``400 Bad Request``. 125 126 The request body or query parameters failed server-side validation. 127 Inspect ``body`` for details. 128 """ 129 130 131class RateLimitError(APIError): 132 """Raised on ``429 Too Many Requests``. 133 134 The client has exceeded the API rate limit. The ``retry_after`` attribute 135 indicates how many seconds to wait before retrying, if the server provided 136 a ``Retry-After`` header. 137 138 Attributes: 139 retry_after: Seconds to wait before retrying, or ``None`` if the 140 server did not include a ``Retry-After`` header. 141 """ 142 143 def __init__( 144 self, 145 status_code: int = 429, 146 body: dict | str | None = None, 147 message: str | None = None, 148 retry_after: float | None = None, 149 *, 150 request_id: str | None = None, 151 ) -> None: 152 super().__init__(status_code, body, message, request_id=request_id) 153 self.retry_after = retry_after 154 155 156class ServerError(APIError): 157 """Raised on ``5xx`` server errors. 158 159 These are typically transient and are automatically retried by the default 160 transport (see `IonQClient`). 161 """ 162 163 164_STATUS_TO_EXCEPTION: dict[int, type[APIError]] = { 165 400: BadRequestError, 166 401: AuthenticationError, 167 403: PermissionDeniedError, 168 404: NotFoundError, 169 429: RateLimitError, 170} 171 172 173def raise_for_status( 174 status_code: int, 175 body: dict | str | None = None, 176 retry_after: float | None = None, 177 message: str | None = None, 178 *, 179 request_id: str | None = None, 180) -> None: 181 """Raise an appropriate `APIError` subclass for an HTTP error status. 182 183 Does nothing for status codes below 400. For 4xx codes, raises the 184 specific subclass (e.g. `AuthenticationError` for 401). For 5xx codes 185 or unrecognized 4xx codes, raises `ServerError` or `APIError` respectively. 186 187 Args: 188 status_code: The HTTP status code. 189 body: The parsed response body. 190 retry_after: Value from the ``Retry-After`` header, if present. 191 message: A human-readable error message. 192 request_id: The ``x-request-id`` response header. 193 194 Raises: 195 BadRequestError: On 400. 196 AuthenticationError: On 401. 197 PermissionDeniedError: On 403. 198 NotFoundError: On 404. 199 RateLimitError: On 429. 200 ServerError: On 5xx. 201 APIError: On other 4xx codes. 202 """ 203 if status_code < 400: 204 return 205 exc_cls = _STATUS_TO_EXCEPTION.get(status_code, ServerError if status_code >= 500 else APIError) 206 if exc_cls is RateLimitError: 207 raise RateLimitError(status_code, body, message, retry_after, request_id=request_id) 208 raise exc_cls(status_code, body, message, request_id=request_id)
58class APIConnectionError(IonQError): 59 """Raised when a connection to the IonQ API cannot be established. 60 61 This covers DNS resolution failures, refused connections, and other 62 network-level errors. The original ``httpx`` exception is chained 63 via ``__cause__``. 64 """
Raised when a connection to the IonQ API cannot be established.
This covers DNS resolution failures, refused connections, and other
network-level errors. The original httpx exception is chained
via __cause__.
75class APIError(IonQError): 76 """Raised when the IonQ API returns an HTTP error response (4xx or 5xx). 77 78 Attributes: 79 status_code: The HTTP status code. 80 body: The parsed response body (``dict`` if JSON, ``str`` otherwise, 81 or ``None`` if the body could not be read). 82 message: A human-readable error message extracted from the response, 83 or a default ``"HTTP <status>"`` string. 84 request_id: The ``x-request-id`` header from the response, useful for 85 contacting IonQ support about a specific request. 86 """ 87 88 def __init__( 89 self, 90 status_code: int, 91 body: dict | str | None = None, 92 message: str | None = None, 93 *, 94 request_id: str | None = None, 95 ) -> None: 96 self.status_code = status_code 97 self.body = body 98 self.request_id = request_id 99 self.message = message or f"HTTP {status_code}" 100 super().__init__(self.message)
Raised when the IonQ API returns an HTTP error response (4xx or 5xx).
Attributes:
- status_code: The HTTP status code.
- body: The parsed response body (
dictif JSON,strotherwise, orNoneif the body could not be read). - message: A human-readable error message extracted from the response,
or a default
"HTTP <status>"string. - request_id: The
x-request-idheader from the response, useful for contacting IonQ support about a specific request.
88 def __init__( 89 self, 90 status_code: int, 91 body: dict | str | None = None, 92 message: str | None = None, 93 *, 94 request_id: str | None = None, 95 ) -> None: 96 self.status_code = status_code 97 self.body = body 98 self.request_id = request_id 99 self.message = message or f"HTTP {status_code}" 100 super().__init__(self.message)
67class APITimeoutError(APIConnectionError): 68 """Raised when a request to the IonQ API times out. 69 70 Inherits from `APIConnectionError` so that catching connection errors 71 also catches timeouts. 72 """
Raised when a request to the IonQ API times out.
Inherits from APIConnectionError so that catching connection errors
also catches timeouts.
103class AuthenticationError(APIError): 104 """Raised on ``401 Unauthorized``. 105 106 Typically means the API key is missing, invalid, or revoked. 107 """
Raised on 401 Unauthorized.
Typically means the API key is missing, invalid, or revoked.
Inherited Members
124class BadRequestError(APIError): 125 """Raised on ``400 Bad Request``. 126 127 The request body or query parameters failed server-side validation. 128 Inspect ``body`` for details. 129 """
Raised on 400 Bad Request.
The request body or query parameters failed server-side validation.
Inspect body for details.
Inherited Members
50class IonQError(Exception): 51 """Base exception for all IonQ errors. 52 53 Catch this to handle any error raised by the library, including connection 54 failures, API errors, polling timeouts, and job failures. 55 """
Base exception for all IonQ errors.
Catch this to handle any error raised by the library, including connection failures, API errors, polling timeouts, and job failures.
117class NotFoundError(APIError): 118 """Raised on ``404 Not Found``. 119 120 The requested resource (job, session, backend, etc.) does not exist. 121 """
Raised on 404 Not Found.
The requested resource (job, session, backend, etc.) does not exist.
Inherited Members
110class PermissionDeniedError(APIError): 111 """Raised on ``403 Forbidden``. 112 113 The API key is valid but lacks permission for the requested operation. 114 """
Raised on 403 Forbidden.
The API key is valid but lacks permission for the requested operation.
Inherited Members
132class RateLimitError(APIError): 133 """Raised on ``429 Too Many Requests``. 134 135 The client has exceeded the API rate limit. The ``retry_after`` attribute 136 indicates how many seconds to wait before retrying, if the server provided 137 a ``Retry-After`` header. 138 139 Attributes: 140 retry_after: Seconds to wait before retrying, or ``None`` if the 141 server did not include a ``Retry-After`` header. 142 """ 143 144 def __init__( 145 self, 146 status_code: int = 429, 147 body: dict | str | None = None, 148 message: str | None = None, 149 retry_after: float | None = None, 150 *, 151 request_id: str | None = None, 152 ) -> None: 153 super().__init__(status_code, body, message, request_id=request_id) 154 self.retry_after = retry_after
Raised on 429 Too Many Requests.
The client has exceeded the API rate limit. The retry_after attribute
indicates how many seconds to wait before retrying, if the server provided
a Retry-After header.
Attributes:
- retry_after: Seconds to wait before retrying, or
Noneif the server did not include aRetry-Afterheader.
144 def __init__( 145 self, 146 status_code: int = 429, 147 body: dict | str | None = None, 148 message: str | None = None, 149 retry_after: float | None = None, 150 *, 151 request_id: str | None = None, 152 ) -> None: 153 super().__init__(status_code, body, message, request_id=request_id) 154 self.retry_after = retry_after
Inherited Members
157class ServerError(APIError): 158 """Raised on ``5xx`` server errors. 159 160 These are typically transient and are automatically retried by the default 161 transport (see `IonQClient`). 162 """
Raised on 5xx server errors.
These are typically transient and are automatically retried by the default
transport (see IonQClient).