boto/boto3 · error · InvalidCrtTransferConfigError

The following transfer config options are invalid when…

Error message

The following transfer config options are invalid when preferred_transfer_client is set to crt: {invalid_crt_args}`

What it means

Raised by boto3's CRT (C Runtime) transfer integration when preferred_transfer_client is set to 'crt' but the TransferConfig also contains options that the CRT client does not honor. boto3 inspects config.DEFAULTS and, for every key not in _ALLOWED_CRT_TRANSFER_CONFIG_OPTIONS (multipart_threshold, max_concurrency, max_request_concurrency, multipart_chunksize, preferred_transfer_client), if the value differs from UNSET_DEFAULT it is collected and rejected. The goal is to fail fast rather than silently ignore a tuning knob the user believes is active.

Solutions

  1. Remove or leave at default every TransferConfig attribute not in the allowed CRT set (multipart_threshold, max_concurrency, max_request_concurrency, multipart_chunksize, preferred_transfer_client).
  2. If you need classic-client-only tuning (use_threads, max_bandwidth), set preferred_transfer_client back to its default (the classic botocore manager) instead of 'crt'.
  3. Inspect the error message's invalid_crt_args list and delete exactly those kwargs from your TransferConfig constructor.

Example fix

// before
config = TransferConfig(
    preferred_transfer_client='crt',
    use_threads=True,
    max_bandwidth=8_000_000,
)
// after
config = TransferConfig(
    preferred_transfer_client='crt',
    multipart_threshold=8 * 1024 * 1024,
    max_concurrency=10,
)
Defensive patterns

Strategy: validation

Validate before calling

ALLOWED = {
    'multipart_threshold', 'max_concurrency',
    'max_request_concurrency', 'multipart_chunksize',
    'preferred_transfer_client',
}
provided = {k: v for k, v in vars(config).items() if v is not None}
bad = [k for k in provided if k not in ALLOWED]
assert not bad, f'Invalid CRT transfer options: {bad}'

Type guard

def is_crt_compatible_config(cfg) -> bool:
    allowed = {'multipart_threshold', 'max_concurrency',
              'max_request_concurrency', 'multipart_chunksize',
              'preferred_transfer_client'}
    return all(k in allowed or getattr(cfg, k, None) is None
               for k in dir(cfg) if not k.startswith('_'))

Try / catch

from boto3.exceptions import InvalidCrtTransferConfigError
try:
    s3.upload_file(src, bucket, key, Config=config)
except InvalidCrtTransferConfigError as e:
    bad = [s for s in str(e).split(':', 1)[1].split('`')[0].split(',') if s.strip()]
    for k in bad:
        setattr(config, k.strip(), None)  # reset and retry with classic client

Prevention

When it happens

Trigger: Calling boto3.client('s3').upload_file/download_file (or any TransferManager-backed operation) with a botocore.transfer.TransferConfig where preferred_transfer_client='crt' and a disallowed attribute (e.g. use_threads, max_bandwidth, num_download_attempts, io_chunksize) is set to a non-default value.

Common situations: Migrating from the default threaded transfer client to the CRT client and reusing the existing TransferConfig verbatim; copy-pasting a tuning recipe written for the classic client; upgrading to a boto3 version that introduced preferred_transfer_client and not pruning the config.

Related errors


AI-assisted analysis of boto/boto3@6e10b029c1 (2026-08-11). Data as JSON: /api/errors/600f47727fc1ff3d. Report an issue: GitHub.

Appendix: source

Thrown at boto3/crt.py:193

def _validate_crt_transfer_config(config):
    if config is None:
        return
    # CRT client can also be configured via `AUTO_RESOLVE_TRANSFER_CLIENT`
    # but it predates this validation. We only validate against CRT client
    # configured via `CRT_TRANSFER_CLIENT` to preserve compatibility.
    if config.preferred_transfer_client != CRT_TRANSFER_CLIENT:
        return
    invalid_crt_args = []
    for param in config.DEFAULTS.keys():
        val = config.get_deep_attr(param)
        if (
            param not in _ALLOWED_CRT_TRANSFER_CONFIG_OPTIONS
            and val is not config.UNSET_DEFAULT
        ):
            invalid_crt_args.append(param)
    if len(invalid_crt_args) > 0:
        raise InvalidCrtTransferConfigError(
            "The following transfer config options are invalid "
            "when preferred_transfer_client is set to crt: "
            f"{', '.join(invalid_crt_args)}`"
        )


def create_crt_transfer_manager(client, config):
    """Create a CRTTransferManager for optimized data transfer."""
    crt_s3_client = get_crt_s3_client(client, config)
    if is_crt_compatible_request(client, crt_s3_client):
        crt_transfer_manager_kwargs = {
            'crt_s3_client': crt_s3_client.crt_client,
            'crt_request_serializer': BOTOCORE_CRT_SERIALIZER,
        }
        if TRANSFER_CONFIG_SUPPORTS_CRT:
            _validate_crt_transfer_config(config)
            crt_transfer_manager_kwargs['config'] = config
        if not TRANSFER_CONFIG_SUPPORTS_CRT and config:

View on GitHub (pinned to 6e10b029c1)