pola-rs/polars · error · ValueError
expiration time in STS response did not contain timezone inf
Error message
expiration time in STS response did not contain timezone information
What it means
CredentialProviderAWS._finish_assume_role (py-polars/src/polars/io/cloud/credential_provider/_providers.py:226-247) calls boto3 STS assume_role and reads response['Credentials']['Expiration'], converting it to a UNIX timestamp via expiry.timestamp(). A naive datetime (tzinfo None) would make that conversion timezone-ambiguous, so polars raises ValueError rather than guessing. Real AWS STS always returns tz-aware datetimes, so this almost always indicates a non-standard STS endpoint.
Source
Thrown at py-polars/src/polars/io/cloud/credential_provider/_providers.py:239
if isinstance(expiry := getattr(creds, "_expiry_time", None), datetime)
else None
)
return creds_dict, expiry
def _finish_assume_role(self, session: Any) -> CredentialProviderFunctionReturn:
assert self.assume_role is not None
client = session.client("sts")
sts_response = client.assume_role(**self.assume_role)
creds = sts_response["Credentials"]
expiry = creds["Expiration"]
if expiry.tzinfo is None:
msg = "expiration time in STS response did not contain timezone information"
raise ValueError(msg)
return {
"aws_access_key_id": creds["AccessKeyId"],
"aws_secret_access_key": creds["SecretAccessKey"],
"aws_session_token": creds["SessionToken"],
}, int(expiry.timestamp())
# Called from Rust, mainly for AWS endpoint_url
def _storage_update_options(self) -> dict[str, str]:
if self._storage_options_has_endpoint_url:
return {}
try:
config = self._session()._session.get_scoped_config()
except ImportError:
return {}
if endpoint_url := config.get("endpoint_url"):View on GitHub (pinned to df599052da)
Solutions
- Fix the mock/endpoint to return tz-aware datetimes: datetime.now(timezone.utc) - matching real AWS behavior
- If a corporate proxy is stripping timezone info, report/fix it upstream
- In test fixtures, build the full Credentials dict including a tz-aware Expiration
Example fix
# before (test stub returning a naive datetime)
sts.assume_role.return_value = {"Credentials": {"Expiration": datetime.now(), ...}}
# after
from datetime import timezone
sts.assume_role.return_value = {"Credentials": {"Expiration": datetime.now(timezone.utc), ...}} Defensive patterns
Strategy: try-catch
Try / catch
try:
df = pl.scan_parquet(path, credential_provider=provider).collect()
except ValueError as e:
if "timezone information" in str(e):
raise RuntimeError(
"STS endpoint returned a naive Expiration; fix the STS mock/proxy"
) from e
raise Prevention
- In tests, always build STS Expiration with datetime.now(timezone.utc)
- Treat naive Expiration datetimes from corporate STS proxies as an upstream bug to report
- Run assume_role paths against real or faithful STS emulators in integration tests
When it happens
Trigger: scanning/writing s3:// with credential_provider=pl.CredentialProviderAWS(assume_role={...}) where the STS endpoint is a mock (moto, stubbed boto3) or a proxy returning an Expiration without a timezone offset.
Common situations: Unit/integration tests that stub boto3 Session.client('sts').assume_role with datetime.now() (naive); custom corporate STS proxies that strip offsets; patched boto3 responses in test fixtures.
Related errors
- boto3 must be installed to use `CredentialProviderAWS`
- unsupported: cannot combine aws_profile with {unhandled_key}
- cannot select columns using key of type {qualified_type_name
- expected {df.width} values when selecting columns by boolean
- index {key} is out of bounds for DataFrame of height {num_ro
AI-assisted analysis of pola-rs/polars@df599052da (2026-08-16).
Data as JSON: /api/errors/247486d780bb5214.
Report an issue: GitHub.