Exception Hierarchy

The Singer SDK defines a structured exception hierarchy rooted at SingerSDKError. Every exception raised by the SDK is a subclass of this root, so you can always catch all SDK-level errors with a single except SingerSDKError clause.

Full hierarchy

SingerSDKError
├── ConfigurationError
│   └── ConfigValidationError
├── DiscoveryError
│   └── InvalidReplicationKeyException
├── MappingError
│   ├── ConformedNameClashException
│   ├── MapExpressionError
│   └── StreamMapConfigError
├── SyncError
│   ├── FatalSyncError
│   │   ├── FatalAPIError
│   │   ├── InvalidStreamSortException
│   │   ├── MissingKeyPropertiesError
│   │   ├── RecordsWithoutSchemaException
│   │   ├── TapStreamConnectionFailure
│   │   └── TooManyRecordsException
│   ├── RetriableSyncError
│   │   └── RetriableAPIError
│   ├── IgnorableSyncError
│   │   ├── IgnorableAPIError
│   │   └── InvalidRecord
│   └── DataError
│       └── InvalidJSONSchema
└── SyncLifecycleSignal
    ├── RequestedAbortException
    │   └── MaxRecordsLimitException
    └── AbortedSyncExceptionBase  (abstract)
        ├── AbortedSyncFailedException
        └── AbortedSyncPausedException

All of these names can be imported from singer_sdk.exceptions.


Exception groups

ConfigurationError

Raised during plugin startup when the provided configuration is invalid or missing required values.

Class

When raised

ConfigValidationError

The config dict fails JSON Schema validation. Carries .errors (list of messages) and .schema (the schema that was checked).

Tap developers — you do not normally raise these directly. The SDK raises ConfigValidationError automatically when Tap.config_jsonschema validation fails.


DiscoveryError

Raised during catalog discovery (the --discover run) before any records are synced.

Class

When raised

DiscoveryError

Generic discovery failure; subclass for specific cases.

InvalidReplicationKeyException

The stream’s replication_key is not present in the stream’s schema properties.


MappingError

Raised when a stream map configuration or expression is invalid.

Class

When raised

MappingError

Generic mapping failure; subclass for specific cases.

MapExpressionError

A Jinja/eval expression in a map definition could not be evaluated.

StreamMapConfigError

The stream map configuration itself is structurally invalid.

ConformedNameClashException

Two or more columns conform to the same output name after name normalization.


SyncError — base for runtime errors

All errors that occur during data extraction or loading inherit from SyncError. They are subdivided by recovery strategy.

Fatal (FatalSyncError)

The SDK should abort the sync immediately and exit with a non-zero code.

Class

When raised

FatalAPIError

An HTTP/API error that cannot be retried (e.g. 403 Forbidden, 400 Bad Request).

TapStreamConnectionFailure

The stream connection was lost and cannot be recovered.

TooManyRecordsException

The query returned more records than max_records allows.

RecordsWithoutSchemaException

A target received RECORD messages before the corresponding SCHEMA message.

MissingKeyPropertiesError

A received record is missing one or more declared key properties.

InvalidStreamSortException

Records arrived out of order, violating the stream’s sort invariant.

Raising in tap code:

from singer_sdk.exceptions import FatalAPIError


def validate_response(self, response: requests.Response) -> None:
    if response.status_code == 403:
        msg = f"Access denied: {response.url}"
        raise FatalAPIError(msg)
    super().validate_response(response)

Retriable (RetriableSyncError)

The SDK should retry the request with exponential backoff. The sync aborts only if all retry attempts are exhausted.

Class

When raised

RetriableAPIError

A transient HTTP/API error that is safe to retry (e.g. 429 Too Many Requests, 503 Service Unavailable). Carries an optional .response attribute.

Raising in tap code:

from singer_sdk.exceptions import RetriableAPIError


def validate_response(self, response: requests.Response) -> None:
    if response.status_code == 429:
        msg = f"Rate limited: {response.url}"
        raise RetriableAPIError(msg, response=response)
    super().validate_response(response)

Ignorable (IgnorableSyncError)

The SDK should log a warning, skip the current record or page, and continue. The sync completes normally (exit code 0).

Class

When raised

IgnorableAPIError

An HTTP/API error for an expected non-fatal response (e.g. 404 on a per-record enrichment endpoint). No retry is attempted.

InvalidRecord

A record fails schema validation. Carries .error_message and .record.

Raising in tap code:

from singer_sdk.exceptions import IgnorableAPIError


def validate_response(self, response: requests.Response) -> None:
    if response.status_code == 404:
        msg = f"Resource not found: {response.url}"
        raise IgnorableAPIError(msg)
    super().validate_response(response)

Data quality (DataError)

Raised when a data quality or schema violation is detected. The SDK logs a warning and continues; severity is configurable.

Class

When raised

InvalidJSONSchema

A stream’s declared JSON Schema is structurally invalid.


SyncLifecycleSignal

These are control-flow signals, not errors. They are raised to manage graceful shutdown. The SDK catches them, emits a final STATE message where possible, and exits cleanly.

Class

When raised

RequestedAbortException

A graceful abort was requested (e.g. SIGTERM).

MaxRecordsLimitException

The --max-records limit was reached.

AbortedSyncExceptionBase

Abstract base; use one of the concrete subclasses below.

AbortedSyncFailedException

The sync stopped in a non-resumable state.

AbortedSyncPausedException

The sync stopped cleanly and emitted a resumable state artifact.


Catching SDK exceptions broadly

from singer_sdk.exceptions import (
    SingerSDKError,
    FatalSyncError,
    RetriableSyncError,
    IgnorableSyncError,
)

try:
    stream.sync()
except IgnorableSyncError as e:
    logger.warning("Skipping: %s", e)
except RetriableSyncError as e:
    # handled automatically by the SDK's backoff decorator
    raise
except FatalSyncError:
    raise
except SingerSDKError:
    # catch-all for any other SDK error
    raise

Choosing the right exception

Situation

Raise

HTTP error, must abort

FatalAPIError

HTTP error, safe to retry

RetriableAPIError

HTTP error, expected / skip silently

IgnorableAPIError

Config value is wrong

ConfigValidationError

Replication key not in schema

InvalidReplicationKeyException

Map expression fails at runtime

MapExpressionError

Record missing primary key

MissingKeyPropertiesError

Record fails schema validation

InvalidRecord