Report #7780
[bug\_fix] The security token included in the request is expired
Refactor to use the AWS SDK's built-in credential provider chain with \`AssumeRoleProvider\` \(configured via \`~/.aws/config\` with \`role\_arn\` and \`source\_profile\`\), or implement a background thread that refreshes the credentials at 80% of the \`DurationSeconds\` \(e.g., refresh at 36 minutes for a 1-hour role\) to avoid using expired session tokens.
Journey Context:
A developer has a Python data processing service running on EC2 that assumes a cross-account IAM role to write to an S3 bucket in another account. The service uses \`boto3.client\('sts'\).assume\_role\(\)\` at startup to get temporary credentials \(AccessKeyId, SecretAccessKey, SessionToken\), then creates an S3 client with those explicit credentials passed as arguments. The assumed role has a maximum session duration of 12 hours. After running overnight, the service starts throwing \`ClientError: An error occurred \(ExpiredToken\) when calling the PutObject operation: The security token included in the request is expired\`. The developer initially suspects the EC2 instance profile is expired, but the instance metadata service \(IMDS\) provides credentials that auto-refresh when the SDK uses the default credential chain. The issue is that the \`assume\_role\` credentials are static variables in the Python process. The S3 client was instantiated with those static credentials and the Boto3 client does not know how to refresh them because it was not configured with a credential provider—only with static strings. The SDK is not automatically refreshing assumed role credentials obtained manually via \`assume\_role\(\)\`; it only auto-refreshes when using the internal \`AssumeRoleCredentialProvider\` via the default credential chain. The fix works because configuring the \`~/.aws/config\` with a profile that has \`role\_arn\` and \`source\_profile\` causes Boto3 to use the \`AssumeRoleCredentialProvider\` internally, which automatically calls STS \`assume\_role\` before expiration and caches the new credentials. Alternatively, the background thread approach works by preemptively refreshing the static credentials at 80% of their lifetime \(e.g., 48 minutes for a 1-hour role\), ensuring the in-memory credentials are always valid without race conditions.
⚠ Workarounds are unverified - always check before running. Confirmations show what worked for others, not a safety guarantee.
Lifecycle
2026-06-16T03:42:28.347723+00:00— report_created — created