airbyte.exceptions

All exceptions used in the PyAirbyte.

This design is modeled after structlog's exceptions, in that we bias towards auto-generated property prints rather than sentence-like string concatenation.

E.g. Instead of this:

Subprocess failed with exit code '1'

We do this:

Subprocess failed. (exit_code=1)

The benefit of this approach is that we can easily support structured logging, and we can easily add new properties to exceptions without having to update all the places where they are raised. We can also support any arbitrary number of properties in exceptions, without spending time on building sentence-like string constructions with optional inputs.

In addition, the following principles are applied for exception class design:

  • All exceptions inherit from a common base class.
  • All exceptions have a message attribute.
  • The first line of the docstring is used as the default message.
  • The default message can be overridden by explicitly setting the message attribute.
  • Exceptions may optionally have a guidance attribute.
  • Exceptions may optionally have a help_url attribute.
  • Rendering is automatically handled by the base class.
  • Any helpful context not defined by the exception class can be passed in the context dict arg.
  • Within reason, avoid sending PII to the exception constructor.
  • Exceptions are dataclasses, so they can be instantiated with keyword arguments.
  • Use the 'from' syntax to chain exceptions when it is helpful to do so. E.g. raise AirbyteConnectorNotFoundError(...) from FileNotFoundError(connector_path)
  • Any exception that adds a new property should also be decorated as @dataclass.
  1# Copyright (c) 2023 Airbyte, Inc., all rights reserved.
  2
  3"""All exceptions used in the PyAirbyte.
  4
  5This design is modeled after structlog's exceptions, in that we bias towards auto-generated
  6property prints rather than sentence-like string concatenation.
  7
  8E.g. Instead of this:
  9
 10> `Subprocess failed with exit code '1'`
 11
 12We do this:
 13
 14> `Subprocess failed. (exit_code=1)`
 15
 16The benefit of this approach is that we can easily support structured logging, and we can
 17easily add new properties to exceptions without having to update all the places where they
 18are raised. We can also support any arbitrary number of properties in exceptions, without spending
 19time on building sentence-like string constructions with optional inputs.
 20
 21
 22In addition, the following principles are applied for exception class design:
 23
 24- All exceptions inherit from a common base class.
 25- All exceptions have a message attribute.
 26- The first line of the docstring is used as the default message.
 27- The default message can be overridden by explicitly setting the message attribute.
 28- Exceptions may optionally have a guidance attribute.
 29- Exceptions may optionally have a help_url attribute.
 30- Rendering is automatically handled by the base class.
 31- Any helpful context not defined by the exception class can be passed in the `context` dict arg.
 32- Within reason, avoid sending PII to the exception constructor.
 33- Exceptions are dataclasses, so they can be instantiated with keyword arguments.
 34- Use the 'from' syntax to chain exceptions when it is helpful to do so.
 35  E.g. `raise AirbyteConnectorNotFoundError(...) from FileNotFoundError(connector_path)`
 36- Any exception that adds a new property should also be decorated as `@dataclass`.
 37"""
 38
 39from __future__ import annotations
 40
 41import logging
 42from dataclasses import dataclass
 43from pathlib import Path
 44from textwrap import indent
 45from typing import TYPE_CHECKING, Any
 46
 47from airbyte.constants import AIRBYTE_PRINT_FULL_ERROR_LOGS
 48
 49
 50if TYPE_CHECKING:
 51    from airbyte._util.api_duck_types import AirbyteApiResponseDuckType
 52    from airbyte.cloud.workspaces import CloudWorkspace
 53
 54
 55NEW_ISSUE_URL = "https://github.com/airbytehq/airbyte/issues/new/choose"
 56DOCS_URL_BASE = "https://airbytehq.github.io/PyAirbyte"
 57DOCS_URL = f"{DOCS_URL_BASE}/airbyte.html"
 58
 59VERTICAL_SEPARATOR = "\n" + "-" * 60
 60
 61
 62# Base error class
 63
 64
 65@dataclass
 66class PyAirbyteError(Exception):
 67    """Base class for exceptions in Airbyte."""
 68
 69    guidance: str | None = None
 70    help_url: str | None = None
 71    log_text: str | list[str] | None = None
 72    log_file: Path | None = None
 73    print_full_log: bool = AIRBYTE_PRINT_FULL_ERROR_LOGS
 74    context: dict[str, Any] | None = None
 75    message: str | None = None
 76    original_exception: Exception | None = None
 77
 78    def get_message(self) -> str:
 79        """Return the best description for the exception.
 80
 81        We resolve the following in order:
 82        1. The message sent to the exception constructor (if provided).
 83        2. The first line of the class's docstring.
 84        """
 85        if self.message:
 86            return self.message
 87
 88        return self.__doc__.split("\n")[0] if self.__doc__ else ""
 89
 90    def __str__(self) -> str:
 91        """Return a string representation of the exception."""
 92        special_properties = [
 93            "message",
 94            "guidance",
 95            "help_url",
 96            "log_text",
 97            "context",
 98            "log_file",
 99            "print_full_log",
100            "original_exception",
101        ]
102        display_properties = {
103            k: v
104            for k, v in self.__dict__.items()
105            if k not in special_properties and not k.startswith("_") and v is not None
106        }
107        display_properties.update(self.context or {})
108        context_str = "\n    ".join(
109            f"{str(k).replace('_', ' ').title()}: {v!r}" for k, v in display_properties.items()
110        )
111        exception_str = (
112            f"{self.get_message()} ({self.__class__.__name__})"
113            + VERTICAL_SEPARATOR
114            + f"\n{self.__class__.__name__}: {self.get_message()}"
115        )
116
117        if self.guidance:
118            exception_str += f"\n    {self.guidance}"
119
120        if self.help_url:
121            exception_str += f"\n    More info: {self.help_url}"
122
123        if context_str:
124            exception_str += "\n    " + context_str
125
126        if self.log_text:
127            if isinstance(self.log_text, list):
128                self.log_text = "\n".join(self.log_text)
129
130            exception_str += f"\n    Log output: \n    {indent(self.log_text, '    ')}"
131
132        if self.original_exception:
133            exception_str += VERTICAL_SEPARATOR + f"\nCaused by: {self.original_exception!s}"
134
135        if self.log_file:
136            if self.print_full_log:
137                if not self.log_file.is_file():
138                    exception_str += f"\n    No log file found at: {self.log_file.absolute()!s}"
139
140                else:
141                    try:
142                        full_log_file_text = self.log_file.read_text()
143                    except Exception as ex:
144                        full_log_file_text = (
145                            f"[ERROR] Log file could not be read from: {self.log_file.absolute()!s}"
146                            f"\nRead error: {ex!s}"
147                        )
148
149                    exception_str += (
150                        f"\n    Full log file text from {self.log_file.absolute()!s}:"
151                        + VERTICAL_SEPARATOR
152                        + full_log_file_text
153                        + VERTICAL_SEPARATOR
154                    )
155            else:
156                exception_str += f"\n    Log file: {self.log_file.absolute()!s}"
157        return exception_str
158
159    def __repr__(self) -> str:
160        """Return a string representation of the exception."""
161        class_name = self.__class__.__name__
162        properties_str = ", ".join(
163            f"{k}={v!r}" for k, v in self.__dict__.items() if not k.startswith("_")
164        )
165        return f"{class_name}({properties_str})"
166
167    def safe_logging_dict(self) -> dict[str, Any]:
168        """Return a dictionary of the exception's properties which is safe for logging.
169
170        We avoid any properties which could potentially contain PII.
171        """
172        result = {
173            # The class name is safe to log:
174            "class": self.__class__.__name__,
175            # We discourage interpolated strings in 'message' so that this should never contain PII:
176            "message": self.get_message(),
177        }
178        safe_attrs = ["connector_name", "stream_name", "violation", "exit_code"]
179        for attr in safe_attrs:
180            if hasattr(self, attr):
181                result[attr] = getattr(self, attr)
182
183        return result
184
185
186# PyAirbyte Internal Errors (these are probably bugs)
187
188
189@dataclass
190class PyAirbyteInternalError(PyAirbyteError):
191    """An internal error occurred in PyAirbyte."""
192
193    guidance = "Please consider reporting this error to the Airbyte team."
194    help_url = NEW_ISSUE_URL
195
196
197# PyAirbyte Input Errors (replaces ValueError for user input)
198
199
200@dataclass
201class PyAirbyteInputError(PyAirbyteError, ValueError):
202    """The input provided to PyAirbyte did not match expected validation rules.
203
204    This inherits from ValueError so that it can be used as a drop-in replacement for
205    ValueError in the PyAirbyte API.
206    """
207
208    guidance = "Please check the provided value and try again."
209    help_url = DOCS_URL
210    input_value: str | None = None
211
212
213@dataclass
214class PyAirbyteNoStreamsSelectedError(PyAirbyteInputError):
215    """No streams were selected for the source."""
216
217    guidance = (
218        "Please call `select_streams()` to select at least one stream from the list provided. "
219        "You can also call `select_all_streams()` to select all available streams for this source."
220    )
221    connector_name: str | None = None
222    available_streams: list[str] | None = None
223
224
225# MCP Server Errors
226
227
228@dataclass
229class AirbyteMCPError(PyAirbyteError):
230    """An error occurred in the Airbyte MCP server."""
231
232
233@dataclass
234class AirbyteTrustedExecutionRequiredError(AirbyteMCPError):
235    """A trusted-execution-only capability was invoked while trusted execution is disabled.
236
237    Trusted execution grants the MCP server its trusted-machine capabilities: local
238    filesystem access, local connector installation/execution, and server-side secret
239    resolution. It defaults to *off* on every transport and is permanently unavailable
240    over the HTTP transport, so a backend helper that exposes one of those capabilities
241    hard-fails when the gate is disabled -- independently of whether the corresponding
242    tool was hidden from the tool listing.
243    """
244
245    guidance = (
246        "Set `AIRBYTE_MCP_TRUSTED_EXECUTION=1` on the MCP server process and restart it. "
247        "Trusted execution is only available on the stdio transport; it can never be "
248        "enabled for an HTTP/hosted deployment."
249    )
250    feature: str | None = None
251
252
253# Normalization Errors
254
255
256@dataclass
257class PyAirbyteNameNormalizationError(PyAirbyteError, ValueError):
258    """Error occurred while normalizing a table or column name."""
259
260    guidance = (
261        "Please consider renaming the source object if possible, or "
262        "raise an issue in GitHub if not."
263    )
264    help_url = NEW_ISSUE_URL
265
266    raw_name: str | None = None
267    normalization_result: str | None = None
268
269
270# PyAirbyte Cache Errors
271
272
273class PyAirbyteCacheError(PyAirbyteError):
274    """Error occurred while accessing the cache."""
275
276
277@dataclass
278class PyAirbyteCacheTableValidationError(PyAirbyteCacheError):
279    """Cache table validation failed."""
280
281    violation: str | None = None
282
283
284@dataclass
285class AirbyteConnectorConfigurationMissingError(PyAirbyteCacheError):
286    """Connector is missing configuration."""
287
288    connector_name: str | None = None
289
290
291# Subprocess Errors
292
293
294@dataclass
295class AirbyteSubprocessError(PyAirbyteError):
296    """Error when running subprocess."""
297
298    run_args: list[str] | None = None
299
300
301@dataclass
302class AirbyteSubprocessFailedError(AirbyteSubprocessError):
303    """Subprocess failed."""
304
305    exit_code: int | None = None
306
307
308# Connector Registry Errors
309
310
311class AirbyteConnectorRegistryError(PyAirbyteError):
312    """Error when accessing the connector registry."""
313
314
315@dataclass
316class AirbyteConnectorNotRegisteredError(AirbyteConnectorRegistryError):
317    """Connector not found in registry."""
318
319    connector_name: str | None = None
320    guidance = (
321        "Please double check the connector name. "
322        "Alternatively, you can provide an explicit connector install method to `get_source()`: "
323        "`pip_url`, `local_executable`, `docker_image`, or `source_manifest`."
324    )
325    help_url = DOCS_URL_BASE + "/airbyte/sources/util.html#get_source"
326
327
328@dataclass
329class AirbyteConnectorNotPyPiPublishedError(AirbyteConnectorRegistryError):
330    """Connector found, but not published to PyPI."""
331
332    connector_name: str | None = None
333    guidance = "This likely means that the connector is not ready for use with PyAirbyte."
334
335
336# Connector Errors
337
338
339@dataclass
340class AirbyteConnectorError(PyAirbyteError):
341    """Error when running the connector."""
342
343    connector_name: str | None = None
344
345    def __post_init__(self) -> None:
346        """Set the log file path for the connector."""
347        self.log_file = self._get_log_file()
348        if not self.guidance and self.log_file:
349            self.guidance = "Please review the log file for more information."
350
351    def _get_log_file(self) -> Path | None:
352        """Return the log file path for the connector."""
353        if self.connector_name:
354            logger = logging.getLogger(f"airbyte.{self.connector_name}")
355
356            log_paths: list[Path] = [
357                Path(handler.baseFilename).absolute()
358                for handler in logger.handlers
359                if isinstance(handler, logging.FileHandler)
360            ]
361
362            if log_paths:
363                return log_paths[0]
364
365        return None
366
367
368class AirbyteConnectorExecutableNotFoundError(AirbyteConnectorError):
369    """Connector executable not found."""
370
371
372class AirbyteConnectorInstallationError(AirbyteConnectorError):
373    """Error when installing the connector."""
374
375
376class AirbyteConnectorReadError(AirbyteConnectorError):
377    """Error when reading from the connector."""
378
379
380class AirbyteConnectorWriteError(AirbyteConnectorError):
381    """Error when writing to the connector."""
382
383
384class AirbyteConnectorSpecFailedError(AirbyteConnectorError):
385    """Error when getting spec from the connector."""
386
387
388class AirbyteConnectorDiscoverFailedError(AirbyteConnectorError):
389    """Error when running discovery on the connector."""
390
391
392class AirbyteNoDataFromConnectorError(AirbyteConnectorError):
393    """No data was provided from the connector."""
394
395
396class AirbyteConnectorMissingCatalogError(AirbyteConnectorError):
397    """Connector did not return a catalog."""
398
399
400class AirbyteConnectorMissingSpecError(AirbyteConnectorError):
401    """Connector did not return a spec."""
402
403
404class AirbyteConnectorValidationFailedError(AirbyteConnectorError):
405    """Connector config validation failed."""
406
407    guidance = (
408        "Please double-check your config and review the validation errors for more information."
409    )
410
411
412class AirbyteConnectorCheckFailedError(AirbyteConnectorError):
413    """Connector check failed."""
414
415    guidance = (
416        "Please double-check your config or review the connector's logs for more information."
417    )
418
419
420@dataclass
421class AirbyteConnectorFailedError(AirbyteConnectorError):
422    """Connector failed."""
423
424    exit_code: int | None = None
425
426
427@dataclass
428class AirbyteStreamNotFoundError(AirbyteConnectorError):
429    """Connector stream not found."""
430
431    stream_name: str | None = None
432    available_streams: list[str] | None = None
433
434
435@dataclass
436class AirbyteStateNotFoundError(AirbyteConnectorError, KeyError):
437    """State entry not found."""
438
439    stream_name: str | None = None
440    available_streams: list[str] | None = None
441
442
443@dataclass
444class PyAirbyteSecretNotFoundError(PyAirbyteError):
445    """Secret not found."""
446
447    guidance = "Please ensure that the secret is set."
448    help_url = (
449        "https://docs.airbyte.com/using-airbyte/airbyte-lib/getting-started#secrets-management"
450    )
451
452    secret_name: str | None = None
453    sources: list[str] | None = None
454
455
456# Airbyte API Errors
457
458
459@dataclass
460class AirbyteError(PyAirbyteError):
461    """An error occurred while communicating with the hosted Airbyte instance."""
462
463    response: AirbyteApiResponseDuckType | None = None
464    """The API response from the failed request."""
465
466    workspace: CloudWorkspace | None = None
467    """The workspace where the error occurred."""
468
469    @property
470    def workspace_url(self) -> str | None:
471        """The URL to the workspace where the error occurred."""
472        if self.workspace:
473            return self.workspace.workspace_url
474
475        return None
476
477
478@dataclass
479class AirbyteConnectionError(AirbyteError):
480    """An connection error occurred while communicating with the hosted Airbyte instance."""
481
482    connection_id: str | None = None
483    """The connection ID where the error occurred."""
484
485    job_id: int | None = None
486    """The job ID where the error occurred (if applicable)."""
487
488    job_status: str | None = None
489    """The latest status of the job where the error occurred (if applicable)."""
490
491    @property
492    def connection_url(self) -> str | None:
493        """The web URL to the connection where the error occurred."""
494        if self.workspace_url and self.connection_id:
495            return f"{self.workspace_url}/connections/{self.connection_id}"
496
497        return None
498
499    @property
500    def job_history_url(self) -> str | None:
501        """The URL to the job history where the error occurred."""
502        if self.connection_url:
503            return f"{self.connection_url}/timeline"
504
505        return None
506
507    @property
508    def job_url(self) -> str | None:
509        """The URL to the job where the error occurred."""
510        if self.job_history_url and self.job_id:
511            return f"{self.job_history_url}#{self.job_id}::0"
512
513        return None
514
515
516@dataclass
517class AirbyteConnectionSyncError(AirbyteConnectionError):
518    """An error occurred while executing the remote Airbyte job."""
519
520
521@dataclass
522class AirbyteConnectionSyncActiveError(AirbyteConnectionError):
523    """State update rejected because a sync is currently running (HTTP 423)."""
524
525
526@dataclass
527class AirbyteWorkspaceMismatchError(AirbyteError):
528    """Resource does not belong to the expected workspace.
529
530    This error is raised when a resource (connection, source, or destination) is fetched
531    from the API and the workspace ID in the response does not match the expected workspace.
532    """
533
534    resource_type: str | None = None
535    """The type of resource (e.g., 'connection', 'source', 'destination')."""
536
537    resource_id: str | None = None
538    """The ID of the resource that was fetched."""
539
540    expected_workspace_id: str | None = None
541    """The workspace ID that was expected."""
542
543    actual_workspace_id: str | None = None
544    """The workspace ID returned by the API."""
545
546
547@dataclass
548class AirbyteWorkspaceNotEmptyError(AirbyteError):
549    """Workspace cannot be deleted because it contains connections."""
550
551    workspace_id: str | None = None
552    """The workspace ID that was expected to be empty."""
553
554    connection_ids: list[str] | None = None
555    """The connection IDs found in the workspace."""
556
557
558@dataclass
559class AirbyteConnectionSyncTimeoutError(AirbyteConnectionSyncError):
560    """An timeout occurred while waiting for the remote Airbyte job to complete."""
561
562    timeout: int | None = None
563    """The timeout in seconds that was reached."""
564
565
566# Airbyte Resource Errors (General)
567
568
569@dataclass
570class AirbyteMissingResourceError(AirbyteError):
571    """Remote Airbyte resources does not exist."""
572
573    resource_type: str | None = None
574    resource_name_or_id: str | None = None
575
576
577@dataclass
578class AirbyteDuplicateResourcesError(AirbyteError):
579    """Process failed because resource name was not unique."""
580
581    resource_type: str | None = None
582    resource_name: str | None = None
583
584
585# Custom Warnings
586@dataclass
587class AirbyteMultipleResourcesError(AirbyteError):
588    """Could not locate the resource because multiple matching resources were found."""
589
590    resource_type: str | None = None
591    resource_name_or_id: str | None = None
592
593
594# Custom Warnings
595
596
597class AirbyteExperimentalFeatureWarning(FutureWarning):
598    """Warning whenever using experimental features in PyAirbyte."""
599
600
601# PyAirbyte Warnings
602
603
604class PyAirbyteWarning(Warning):
605    """General warnings from PyAirbyte."""
606
607
608class PyAirbyteDataLossWarning(PyAirbyteWarning):
609    """Warning for potential data loss.
610
611    Users can ignore this warning by running:
612    > warnings.filterwarnings("ignore", category="airbyte.exceptions.PyAirbyteDataLossWarning")
613    """
NEW_ISSUE_URL = 'https://github.com/airbytehq/airbyte/issues/new/choose'
DOCS_URL_BASE = 'https://airbytehq.github.io/PyAirbyte'
DOCS_URL = 'https://airbytehq.github.io/PyAirbyte/airbyte.html'
VERTICAL_SEPARATOR = '\n------------------------------------------------------------'
@dataclass
class PyAirbyteError(builtins.Exception):
 66@dataclass
 67class PyAirbyteError(Exception):
 68    """Base class for exceptions in Airbyte."""
 69
 70    guidance: str | None = None
 71    help_url: str | None = None
 72    log_text: str | list[str] | None = None
 73    log_file: Path | None = None
 74    print_full_log: bool = AIRBYTE_PRINT_FULL_ERROR_LOGS
 75    context: dict[str, Any] | None = None
 76    message: str | None = None
 77    original_exception: Exception | None = None
 78
 79    def get_message(self) -> str:
 80        """Return the best description for the exception.
 81
 82        We resolve the following in order:
 83        1. The message sent to the exception constructor (if provided).
 84        2. The first line of the class's docstring.
 85        """
 86        if self.message:
 87            return self.message
 88
 89        return self.__doc__.split("\n")[0] if self.__doc__ else ""
 90
 91    def __str__(self) -> str:
 92        """Return a string representation of the exception."""
 93        special_properties = [
 94            "message",
 95            "guidance",
 96            "help_url",
 97            "log_text",
 98            "context",
 99            "log_file",
100            "print_full_log",
101            "original_exception",
102        ]
103        display_properties = {
104            k: v
105            for k, v in self.__dict__.items()
106            if k not in special_properties and not k.startswith("_") and v is not None
107        }
108        display_properties.update(self.context or {})
109        context_str = "\n    ".join(
110            f"{str(k).replace('_', ' ').title()}: {v!r}" for k, v in display_properties.items()
111        )
112        exception_str = (
113            f"{self.get_message()} ({self.__class__.__name__})"
114            + VERTICAL_SEPARATOR
115            + f"\n{self.__class__.__name__}: {self.get_message()}"
116        )
117
118        if self.guidance:
119            exception_str += f"\n    {self.guidance}"
120
121        if self.help_url:
122            exception_str += f"\n    More info: {self.help_url}"
123
124        if context_str:
125            exception_str += "\n    " + context_str
126
127        if self.log_text:
128            if isinstance(self.log_text, list):
129                self.log_text = "\n".join(self.log_text)
130
131            exception_str += f"\n    Log output: \n    {indent(self.log_text, '    ')}"
132
133        if self.original_exception:
134            exception_str += VERTICAL_SEPARATOR + f"\nCaused by: {self.original_exception!s}"
135
136        if self.log_file:
137            if self.print_full_log:
138                if not self.log_file.is_file():
139                    exception_str += f"\n    No log file found at: {self.log_file.absolute()!s}"
140
141                else:
142                    try:
143                        full_log_file_text = self.log_file.read_text()
144                    except Exception as ex:
145                        full_log_file_text = (
146                            f"[ERROR] Log file could not be read from: {self.log_file.absolute()!s}"
147                            f"\nRead error: {ex!s}"
148                        )
149
150                    exception_str += (
151                        f"\n    Full log file text from {self.log_file.absolute()!s}:"
152                        + VERTICAL_SEPARATOR
153                        + full_log_file_text
154                        + VERTICAL_SEPARATOR
155                    )
156            else:
157                exception_str += f"\n    Log file: {self.log_file.absolute()!s}"
158        return exception_str
159
160    def __repr__(self) -> str:
161        """Return a string representation of the exception."""
162        class_name = self.__class__.__name__
163        properties_str = ", ".join(
164            f"{k}={v!r}" for k, v in self.__dict__.items() if not k.startswith("_")
165        )
166        return f"{class_name}({properties_str})"
167
168    def safe_logging_dict(self) -> dict[str, Any]:
169        """Return a dictionary of the exception's properties which is safe for logging.
170
171        We avoid any properties which could potentially contain PII.
172        """
173        result = {
174            # The class name is safe to log:
175            "class": self.__class__.__name__,
176            # We discourage interpolated strings in 'message' so that this should never contain PII:
177            "message": self.get_message(),
178        }
179        safe_attrs = ["connector_name", "stream_name", "violation", "exit_code"]
180        for attr in safe_attrs:
181            if hasattr(self, attr):
182                result[attr] = getattr(self, attr)
183
184        return result

Base class for exceptions in Airbyte.

PyAirbyteError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None)
guidance: str | None = None
help_url: str | None = None
log_text: str | list[str] | None = None
log_file: pathlib.Path | None = None
print_full_log: bool = True
context: dict[str, typing.Any] | None = None
message: str | None = None
original_exception: Exception | None = None
def get_message(self) -> str:
79    def get_message(self) -> str:
80        """Return the best description for the exception.
81
82        We resolve the following in order:
83        1. The message sent to the exception constructor (if provided).
84        2. The first line of the class's docstring.
85        """
86        if self.message:
87            return self.message
88
89        return self.__doc__.split("\n")[0] if self.__doc__ else ""

Return the best description for the exception.

We resolve the following in order:

  1. The message sent to the exception constructor (if provided).
  2. The first line of the class's docstring.
def safe_logging_dict(self) -> dict[str, typing.Any]:
168    def safe_logging_dict(self) -> dict[str, Any]:
169        """Return a dictionary of the exception's properties which is safe for logging.
170
171        We avoid any properties which could potentially contain PII.
172        """
173        result = {
174            # The class name is safe to log:
175            "class": self.__class__.__name__,
176            # We discourage interpolated strings in 'message' so that this should never contain PII:
177            "message": self.get_message(),
178        }
179        safe_attrs = ["connector_name", "stream_name", "violation", "exit_code"]
180        for attr in safe_attrs:
181            if hasattr(self, attr):
182                result[attr] = getattr(self, attr)
183
184        return result

Return a dictionary of the exception's properties which is safe for logging.

We avoid any properties which could potentially contain PII.

@dataclass
class PyAirbyteInternalError(PyAirbyteError):
190@dataclass
191class PyAirbyteInternalError(PyAirbyteError):
192    """An internal error occurred in PyAirbyte."""
193
194    guidance = "Please consider reporting this error to the Airbyte team."
195    help_url = NEW_ISSUE_URL

An internal error occurred in PyAirbyte.

PyAirbyteInternalError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None)
guidance = 'Please consider reporting this error to the Airbyte team.'
help_url = 'https://github.com/airbytehq/airbyte/issues/new/choose'
@dataclass
class PyAirbyteInputError(PyAirbyteError, builtins.ValueError):
201@dataclass
202class PyAirbyteInputError(PyAirbyteError, ValueError):
203    """The input provided to PyAirbyte did not match expected validation rules.
204
205    This inherits from ValueError so that it can be used as a drop-in replacement for
206    ValueError in the PyAirbyte API.
207    """
208
209    guidance = "Please check the provided value and try again."
210    help_url = DOCS_URL
211    input_value: str | None = None

The input provided to PyAirbyte did not match expected validation rules.

This inherits from ValueError so that it can be used as a drop-in replacement for ValueError in the PyAirbyte API.

PyAirbyteInputError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, input_value: str | None = None)
guidance = 'Please check the provided value and try again.'
help_url = 'https://airbytehq.github.io/PyAirbyte/airbyte.html'
input_value: str | None = None
@dataclass
class PyAirbyteNoStreamsSelectedError(PyAirbyteInputError):
214@dataclass
215class PyAirbyteNoStreamsSelectedError(PyAirbyteInputError):
216    """No streams were selected for the source."""
217
218    guidance = (
219        "Please call `select_streams()` to select at least one stream from the list provided. "
220        "You can also call `select_all_streams()` to select all available streams for this source."
221    )
222    connector_name: str | None = None
223    available_streams: list[str] | None = None

No streams were selected for the source.

PyAirbyteNoStreamsSelectedError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, input_value: str | None = None, connector_name: str | None = None, available_streams: list[str] | None = None)
guidance = 'Please call `select_streams()` to select at least one stream from the list provided. You can also call `select_all_streams()` to select all available streams for this source.'
connector_name: str | None = None
available_streams: list[str] | None = None
@dataclass
class AirbyteMCPError(PyAirbyteError):
229@dataclass
230class AirbyteMCPError(PyAirbyteError):
231    """An error occurred in the Airbyte MCP server."""

An error occurred in the Airbyte MCP server.

AirbyteMCPError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None)
@dataclass
class AirbyteTrustedExecutionRequiredError(AirbyteMCPError):
234@dataclass
235class AirbyteTrustedExecutionRequiredError(AirbyteMCPError):
236    """A trusted-execution-only capability was invoked while trusted execution is disabled.
237
238    Trusted execution grants the MCP server its trusted-machine capabilities: local
239    filesystem access, local connector installation/execution, and server-side secret
240    resolution. It defaults to *off* on every transport and is permanently unavailable
241    over the HTTP transport, so a backend helper that exposes one of those capabilities
242    hard-fails when the gate is disabled -- independently of whether the corresponding
243    tool was hidden from the tool listing.
244    """
245
246    guidance = (
247        "Set `AIRBYTE_MCP_TRUSTED_EXECUTION=1` on the MCP server process and restart it. "
248        "Trusted execution is only available on the stdio transport; it can never be "
249        "enabled for an HTTP/hosted deployment."
250    )
251    feature: str | None = None

A trusted-execution-only capability was invoked while trusted execution is disabled.

Trusted execution grants the MCP server its trusted-machine capabilities: local filesystem access, local connector installation/execution, and server-side secret resolution. It defaults to off on every transport and is permanently unavailable over the HTTP transport, so a backend helper that exposes one of those capabilities hard-fails when the gate is disabled -- independently of whether the corresponding tool was hidden from the tool listing.

AirbyteTrustedExecutionRequiredError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, feature: str | None = None)
guidance = 'Set `AIRBYTE_MCP_TRUSTED_EXECUTION=1` on the MCP server process and restart it. Trusted execution is only available on the stdio transport; it can never be enabled for an HTTP/hosted deployment.'
feature: str | None = None
@dataclass
class PyAirbyteNameNormalizationError(PyAirbyteError, builtins.ValueError):
257@dataclass
258class PyAirbyteNameNormalizationError(PyAirbyteError, ValueError):
259    """Error occurred while normalizing a table or column name."""
260
261    guidance = (
262        "Please consider renaming the source object if possible, or "
263        "raise an issue in GitHub if not."
264    )
265    help_url = NEW_ISSUE_URL
266
267    raw_name: str | None = None
268    normalization_result: str | None = None

Error occurred while normalizing a table or column name.

PyAirbyteNameNormalizationError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, raw_name: str | None = None, normalization_result: str | None = None)
guidance = 'Please consider renaming the source object if possible, or raise an issue in GitHub if not.'
help_url = 'https://github.com/airbytehq/airbyte/issues/new/choose'
raw_name: str | None = None
normalization_result: str | None = None
class PyAirbyteCacheError(PyAirbyteError):
274class PyAirbyteCacheError(PyAirbyteError):
275    """Error occurred while accessing the cache."""

Error occurred while accessing the cache.

@dataclass
class PyAirbyteCacheTableValidationError(PyAirbyteCacheError):
278@dataclass
279class PyAirbyteCacheTableValidationError(PyAirbyteCacheError):
280    """Cache table validation failed."""
281
282    violation: str | None = None

Cache table validation failed.

PyAirbyteCacheTableValidationError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, violation: str | None = None)
violation: str | None = None
@dataclass
class AirbyteConnectorConfigurationMissingError(PyAirbyteCacheError):
285@dataclass
286class AirbyteConnectorConfigurationMissingError(PyAirbyteCacheError):
287    """Connector is missing configuration."""
288
289    connector_name: str | None = None

Connector is missing configuration.

AirbyteConnectorConfigurationMissingError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, connector_name: str | None = None)
connector_name: str | None = None
@dataclass
class AirbyteSubprocessError(PyAirbyteError):
295@dataclass
296class AirbyteSubprocessError(PyAirbyteError):
297    """Error when running subprocess."""
298
299    run_args: list[str] | None = None

Error when running subprocess.

AirbyteSubprocessError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, run_args: list[str] | None = None)
run_args: list[str] | None = None
@dataclass
class AirbyteSubprocessFailedError(AirbyteSubprocessError):
302@dataclass
303class AirbyteSubprocessFailedError(AirbyteSubprocessError):
304    """Subprocess failed."""
305
306    exit_code: int | None = None

Subprocess failed.

AirbyteSubprocessFailedError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, run_args: list[str] | None = None, exit_code: int | None = None)
exit_code: int | None = None
class AirbyteConnectorRegistryError(PyAirbyteError):
312class AirbyteConnectorRegistryError(PyAirbyteError):
313    """Error when accessing the connector registry."""

Error when accessing the connector registry.

@dataclass
class AirbyteConnectorNotRegisteredError(AirbyteConnectorRegistryError):
316@dataclass
317class AirbyteConnectorNotRegisteredError(AirbyteConnectorRegistryError):
318    """Connector not found in registry."""
319
320    connector_name: str | None = None
321    guidance = (
322        "Please double check the connector name. "
323        "Alternatively, you can provide an explicit connector install method to `get_source()`: "
324        "`pip_url`, `local_executable`, `docker_image`, or `source_manifest`."
325    )
326    help_url = DOCS_URL_BASE + "/airbyte/sources/util.html#get_source"

Connector not found in registry.

AirbyteConnectorNotRegisteredError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, connector_name: str | None = None)
connector_name: str | None = None
guidance = 'Please double check the connector name. Alternatively, you can provide an explicit connector install method to `get_source()`: `pip_url`, `local_executable`, `docker_image`, or `source_manifest`.'
help_url = 'https://airbytehq.github.io/PyAirbyte/airbyte/sources/util.html#get_source'
@dataclass
class AirbyteConnectorNotPyPiPublishedError(AirbyteConnectorRegistryError):
329@dataclass
330class AirbyteConnectorNotPyPiPublishedError(AirbyteConnectorRegistryError):
331    """Connector found, but not published to PyPI."""
332
333    connector_name: str | None = None
334    guidance = "This likely means that the connector is not ready for use with PyAirbyte."

Connector found, but not published to PyPI.

AirbyteConnectorNotPyPiPublishedError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, connector_name: str | None = None)
connector_name: str | None = None
guidance = 'This likely means that the connector is not ready for use with PyAirbyte.'
@dataclass
class AirbyteConnectorError(PyAirbyteError):
340@dataclass
341class AirbyteConnectorError(PyAirbyteError):
342    """Error when running the connector."""
343
344    connector_name: str | None = None
345
346    def __post_init__(self) -> None:
347        """Set the log file path for the connector."""
348        self.log_file = self._get_log_file()
349        if not self.guidance and self.log_file:
350            self.guidance = "Please review the log file for more information."
351
352    def _get_log_file(self) -> Path | None:
353        """Return the log file path for the connector."""
354        if self.connector_name:
355            logger = logging.getLogger(f"airbyte.{self.connector_name}")
356
357            log_paths: list[Path] = [
358                Path(handler.baseFilename).absolute()
359                for handler in logger.handlers
360                if isinstance(handler, logging.FileHandler)
361            ]
362
363            if log_paths:
364                return log_paths[0]
365
366        return None

Error when running the connector.

AirbyteConnectorError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, connector_name: str | None = None)
connector_name: str | None = None
class AirbyteConnectorExecutableNotFoundError(AirbyteConnectorError):
369class AirbyteConnectorExecutableNotFoundError(AirbyteConnectorError):
370    """Connector executable not found."""

Connector executable not found.

class AirbyteConnectorInstallationError(AirbyteConnectorError):
373class AirbyteConnectorInstallationError(AirbyteConnectorError):
374    """Error when installing the connector."""

Error when installing the connector.

class AirbyteConnectorReadError(AirbyteConnectorError):
377class AirbyteConnectorReadError(AirbyteConnectorError):
378    """Error when reading from the connector."""

Error when reading from the connector.

class AirbyteConnectorWriteError(AirbyteConnectorError):
381class AirbyteConnectorWriteError(AirbyteConnectorError):
382    """Error when writing to the connector."""

Error when writing to the connector.

class AirbyteConnectorSpecFailedError(AirbyteConnectorError):
385class AirbyteConnectorSpecFailedError(AirbyteConnectorError):
386    """Error when getting spec from the connector."""

Error when getting spec from the connector.

class AirbyteConnectorDiscoverFailedError(AirbyteConnectorError):
389class AirbyteConnectorDiscoverFailedError(AirbyteConnectorError):
390    """Error when running discovery on the connector."""

Error when running discovery on the connector.

class AirbyteNoDataFromConnectorError(AirbyteConnectorError):
393class AirbyteNoDataFromConnectorError(AirbyteConnectorError):
394    """No data was provided from the connector."""

No data was provided from the connector.

class AirbyteConnectorMissingCatalogError(AirbyteConnectorError):
397class AirbyteConnectorMissingCatalogError(AirbyteConnectorError):
398    """Connector did not return a catalog."""

Connector did not return a catalog.

class AirbyteConnectorMissingSpecError(AirbyteConnectorError):
401class AirbyteConnectorMissingSpecError(AirbyteConnectorError):
402    """Connector did not return a spec."""

Connector did not return a spec.

class AirbyteConnectorValidationFailedError(AirbyteConnectorError):
405class AirbyteConnectorValidationFailedError(AirbyteConnectorError):
406    """Connector config validation failed."""
407
408    guidance = (
409        "Please double-check your config and review the validation errors for more information."
410    )

Connector config validation failed.

guidance = 'Please double-check your config and review the validation errors for more information.'
class AirbyteConnectorCheckFailedError(AirbyteConnectorError):
413class AirbyteConnectorCheckFailedError(AirbyteConnectorError):
414    """Connector check failed."""
415
416    guidance = (
417        "Please double-check your config or review the connector's logs for more information."
418    )

Connector check failed.

guidance = "Please double-check your config or review the connector's logs for more information."
@dataclass
class AirbyteConnectorFailedError(AirbyteConnectorError):
421@dataclass
422class AirbyteConnectorFailedError(AirbyteConnectorError):
423    """Connector failed."""
424
425    exit_code: int | None = None

Connector failed.

AirbyteConnectorFailedError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, connector_name: str | None = None, exit_code: int | None = None)
exit_code: int | None = None
@dataclass
class AirbyteStreamNotFoundError(AirbyteConnectorError):
428@dataclass
429class AirbyteStreamNotFoundError(AirbyteConnectorError):
430    """Connector stream not found."""
431
432    stream_name: str | None = None
433    available_streams: list[str] | None = None

Connector stream not found.

AirbyteStreamNotFoundError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, connector_name: str | None = None, stream_name: str | None = None, available_streams: list[str] | None = None)
stream_name: str | None = None
available_streams: list[str] | None = None
@dataclass
class AirbyteStateNotFoundError(AirbyteConnectorError, builtins.KeyError):
436@dataclass
437class AirbyteStateNotFoundError(AirbyteConnectorError, KeyError):
438    """State entry not found."""
439
440    stream_name: str | None = None
441    available_streams: list[str] | None = None

State entry not found.

AirbyteStateNotFoundError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, connector_name: str | None = None, stream_name: str | None = None, available_streams: list[str] | None = None)
stream_name: str | None = None
available_streams: list[str] | None = None
@dataclass
class PyAirbyteSecretNotFoundError(PyAirbyteError):
444@dataclass
445class PyAirbyteSecretNotFoundError(PyAirbyteError):
446    """Secret not found."""
447
448    guidance = "Please ensure that the secret is set."
449    help_url = (
450        "https://docs.airbyte.com/using-airbyte/airbyte-lib/getting-started#secrets-management"
451    )
452
453    secret_name: str | None = None
454    sources: list[str] | None = None

Secret not found.

PyAirbyteSecretNotFoundError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, secret_name: str | None = None, sources: list[str] | None = None)
guidance = 'Please ensure that the secret is set.'
help_url = 'https://docs.airbyte.com/using-airbyte/airbyte-lib/getting-started#secrets-management'
secret_name: str | None = None
sources: list[str] | None = None
@dataclass
class AirbyteError(PyAirbyteError):
460@dataclass
461class AirbyteError(PyAirbyteError):
462    """An error occurred while communicating with the hosted Airbyte instance."""
463
464    response: AirbyteApiResponseDuckType | None = None
465    """The API response from the failed request."""
466
467    workspace: CloudWorkspace | None = None
468    """The workspace where the error occurred."""
469
470    @property
471    def workspace_url(self) -> str | None:
472        """The URL to the workspace where the error occurred."""
473        if self.workspace:
474            return self.workspace.workspace_url
475
476        return None

An error occurred while communicating with the hosted Airbyte instance.

AirbyteError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, response: airbyte._util.api_duck_types.AirbyteApiResponseDuckType | None = None, workspace: airbyte.cloud.CloudWorkspace | None = None)
response: airbyte._util.api_duck_types.AirbyteApiResponseDuckType | None = None

The API response from the failed request.

workspace: airbyte.cloud.CloudWorkspace | None = None

The workspace where the error occurred.

workspace_url: str | None
470    @property
471    def workspace_url(self) -> str | None:
472        """The URL to the workspace where the error occurred."""
473        if self.workspace:
474            return self.workspace.workspace_url
475
476        return None

The URL to the workspace where the error occurred.

@dataclass
class AirbyteConnectionError(AirbyteError):
479@dataclass
480class AirbyteConnectionError(AirbyteError):
481    """An connection error occurred while communicating with the hosted Airbyte instance."""
482
483    connection_id: str | None = None
484    """The connection ID where the error occurred."""
485
486    job_id: int | None = None
487    """The job ID where the error occurred (if applicable)."""
488
489    job_status: str | None = None
490    """The latest status of the job where the error occurred (if applicable)."""
491
492    @property
493    def connection_url(self) -> str | None:
494        """The web URL to the connection where the error occurred."""
495        if self.workspace_url and self.connection_id:
496            return f"{self.workspace_url}/connections/{self.connection_id}"
497
498        return None
499
500    @property
501    def job_history_url(self) -> str | None:
502        """The URL to the job history where the error occurred."""
503        if self.connection_url:
504            return f"{self.connection_url}/timeline"
505
506        return None
507
508    @property
509    def job_url(self) -> str | None:
510        """The URL to the job where the error occurred."""
511        if self.job_history_url and self.job_id:
512            return f"{self.job_history_url}#{self.job_id}::0"
513
514        return None

An connection error occurred while communicating with the hosted Airbyte instance.

AirbyteConnectionError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, response: airbyte._util.api_duck_types.AirbyteApiResponseDuckType | None = None, workspace: airbyte.cloud.CloudWorkspace | None = None, connection_id: str | None = None, job_id: int | None = None, job_status: str | None = None)
connection_id: str | None = None

The connection ID where the error occurred.

job_id: int | None = None

The job ID where the error occurred (if applicable).

job_status: str | None = None

The latest status of the job where the error occurred (if applicable).

connection_url: str | None
492    @property
493    def connection_url(self) -> str | None:
494        """The web URL to the connection where the error occurred."""
495        if self.workspace_url and self.connection_id:
496            return f"{self.workspace_url}/connections/{self.connection_id}"
497
498        return None

The web URL to the connection where the error occurred.

job_history_url: str | None
500    @property
501    def job_history_url(self) -> str | None:
502        """The URL to the job history where the error occurred."""
503        if self.connection_url:
504            return f"{self.connection_url}/timeline"
505
506        return None

The URL to the job history where the error occurred.

job_url: str | None
508    @property
509    def job_url(self) -> str | None:
510        """The URL to the job where the error occurred."""
511        if self.job_history_url and self.job_id:
512            return f"{self.job_history_url}#{self.job_id}::0"
513
514        return None

The URL to the job where the error occurred.

@dataclass
class AirbyteConnectionSyncError(AirbyteConnectionError):
517@dataclass
518class AirbyteConnectionSyncError(AirbyteConnectionError):
519    """An error occurred while executing the remote Airbyte job."""

An error occurred while executing the remote Airbyte job.

AirbyteConnectionSyncError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, response: airbyte._util.api_duck_types.AirbyteApiResponseDuckType | None = None, workspace: airbyte.cloud.CloudWorkspace | None = None, connection_id: str | None = None, job_id: int | None = None, job_status: str | None = None)
@dataclass
class AirbyteConnectionSyncActiveError(AirbyteConnectionError):
522@dataclass
523class AirbyteConnectionSyncActiveError(AirbyteConnectionError):
524    """State update rejected because a sync is currently running (HTTP 423)."""

State update rejected because a sync is currently running (HTTP 423).

AirbyteConnectionSyncActiveError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, response: airbyte._util.api_duck_types.AirbyteApiResponseDuckType | None = None, workspace: airbyte.cloud.CloudWorkspace | None = None, connection_id: str | None = None, job_id: int | None = None, job_status: str | None = None)
@dataclass
class AirbyteWorkspaceMismatchError(AirbyteError):
527@dataclass
528class AirbyteWorkspaceMismatchError(AirbyteError):
529    """Resource does not belong to the expected workspace.
530
531    This error is raised when a resource (connection, source, or destination) is fetched
532    from the API and the workspace ID in the response does not match the expected workspace.
533    """
534
535    resource_type: str | None = None
536    """The type of resource (e.g., 'connection', 'source', 'destination')."""
537
538    resource_id: str | None = None
539    """The ID of the resource that was fetched."""
540
541    expected_workspace_id: str | None = None
542    """The workspace ID that was expected."""
543
544    actual_workspace_id: str | None = None
545    """The workspace ID returned by the API."""

Resource does not belong to the expected workspace.

This error is raised when a resource (connection, source, or destination) is fetched from the API and the workspace ID in the response does not match the expected workspace.

AirbyteWorkspaceMismatchError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, response: airbyte._util.api_duck_types.AirbyteApiResponseDuckType | None = None, workspace: airbyte.cloud.CloudWorkspace | None = None, resource_type: str | None = None, resource_id: str | None = None, expected_workspace_id: str | None = None, actual_workspace_id: str | None = None)
resource_type: str | None = None

The type of resource (e.g., 'connection', 'source', 'destination').

resource_id: str | None = None

The ID of the resource that was fetched.

expected_workspace_id: str | None = None

The workspace ID that was expected.

actual_workspace_id: str | None = None

The workspace ID returned by the API.

@dataclass
class AirbyteWorkspaceNotEmptyError(AirbyteError):
548@dataclass
549class AirbyteWorkspaceNotEmptyError(AirbyteError):
550    """Workspace cannot be deleted because it contains connections."""
551
552    workspace_id: str | None = None
553    """The workspace ID that was expected to be empty."""
554
555    connection_ids: list[str] | None = None
556    """The connection IDs found in the workspace."""

Workspace cannot be deleted because it contains connections.

AirbyteWorkspaceNotEmptyError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, response: airbyte._util.api_duck_types.AirbyteApiResponseDuckType | None = None, workspace: airbyte.cloud.CloudWorkspace | None = None, workspace_id: str | None = None, connection_ids: list[str] | None = None)
workspace_id: str | None = None

The workspace ID that was expected to be empty.

connection_ids: list[str] | None = None

The connection IDs found in the workspace.

@dataclass
class AirbyteConnectionSyncTimeoutError(AirbyteConnectionSyncError):
559@dataclass
560class AirbyteConnectionSyncTimeoutError(AirbyteConnectionSyncError):
561    """An timeout occurred while waiting for the remote Airbyte job to complete."""
562
563    timeout: int | None = None
564    """The timeout in seconds that was reached."""

An timeout occurred while waiting for the remote Airbyte job to complete.

AirbyteConnectionSyncTimeoutError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, response: airbyte._util.api_duck_types.AirbyteApiResponseDuckType | None = None, workspace: airbyte.cloud.CloudWorkspace | None = None, connection_id: str | None = None, job_id: int | None = None, job_status: str | None = None, timeout: int | None = None)
timeout: int | None = None

The timeout in seconds that was reached.

@dataclass
class AirbyteMissingResourceError(AirbyteError):
570@dataclass
571class AirbyteMissingResourceError(AirbyteError):
572    """Remote Airbyte resources does not exist."""
573
574    resource_type: str | None = None
575    resource_name_or_id: str | None = None

Remote Airbyte resources does not exist.

AirbyteMissingResourceError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, response: airbyte._util.api_duck_types.AirbyteApiResponseDuckType | None = None, workspace: airbyte.cloud.CloudWorkspace | None = None, resource_type: str | None = None, resource_name_or_id: str | None = None)
resource_type: str | None = None
resource_name_or_id: str | None = None
@dataclass
class AirbyteDuplicateResourcesError(AirbyteError):
578@dataclass
579class AirbyteDuplicateResourcesError(AirbyteError):
580    """Process failed because resource name was not unique."""
581
582    resource_type: str | None = None
583    resource_name: str | None = None

Process failed because resource name was not unique.

AirbyteDuplicateResourcesError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, response: airbyte._util.api_duck_types.AirbyteApiResponseDuckType | None = None, workspace: airbyte.cloud.CloudWorkspace | None = None, resource_type: str | None = None, resource_name: str | None = None)
resource_type: str | None = None
resource_name: str | None = None
@dataclass
class AirbyteMultipleResourcesError(AirbyteError):
587@dataclass
588class AirbyteMultipleResourcesError(AirbyteError):
589    """Could not locate the resource because multiple matching resources were found."""
590
591    resource_type: str | None = None
592    resource_name_or_id: str | None = None

Could not locate the resource because multiple matching resources were found.

AirbyteMultipleResourcesError( guidance: str | None = None, help_url: str | None = None, log_text: str | list[str] | None = None, log_file: pathlib.Path | None = None, print_full_log: bool = True, context: dict[str, typing.Any] | None = None, message: str | None = None, original_exception: Exception | None = None, response: airbyte._util.api_duck_types.AirbyteApiResponseDuckType | None = None, workspace: airbyte.cloud.CloudWorkspace | None = None, resource_type: str | None = None, resource_name_or_id: str | None = None)
resource_type: str | None = None
resource_name_or_id: str | None = None
class AirbyteExperimentalFeatureWarning(builtins.FutureWarning):
598class AirbyteExperimentalFeatureWarning(FutureWarning):
599    """Warning whenever using experimental features in PyAirbyte."""

Warning whenever using experimental features in PyAirbyte.

class PyAirbyteWarning(builtins.Warning):
605class PyAirbyteWarning(Warning):
606    """General warnings from PyAirbyte."""

General warnings from PyAirbyte.

class PyAirbyteDataLossWarning(PyAirbyteWarning):
609class PyAirbyteDataLossWarning(PyAirbyteWarning):
610    """Warning for potential data loss.
611
612    Users can ignore this warning by running:
613    > warnings.filterwarnings("ignore", category="airbyte.exceptions.PyAirbyteDataLossWarning")
614    """

Warning for potential data loss.

Users can ignore this warning by running:

warnings.filterwarnings("ignore", category="airbyte.exceptions.PyAirbyteDataLossWarning")