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
- 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).
- 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'.
- 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
- Keep a single TransferConfig factory that branches on preferred_transfer_client.
- Document which TransferConfig fields each transfer client supports.
- Unit-test config construction against _ALLOWED_CRT_TRANSFER_CONFIG_OPTIONS.
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
- CRT transfer client is configured but is missing minimum…
- Either a boto3.Client or s3transfer.manager.TransferManager…
- Manager cannot be provided with client, config, nor osutil…
- Max Retries Exceeded
- AND operation cannot be applied to value
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)