elastic/elasticsearch · error · IllegalStateException
STS AssumeRoleWithWebIdentity response did not include a cre
Error message
STS AssumeRoleWithWebIdentity response did not include a credential expiry
What it means
After Credentials is present, toCached requires credentials.expiration() to be non-null. The expiry drives prefetchAt and staleAt computation; without it the cache cannot know when to refresh. A missing expiry is treated as an illegal state.
Source
Thrown at libs/workload-identity-aws/src/main/java/org/elasticsearch/workload/identity/aws/AsyncWebIdentityCredentialsProvider.java:202
}
private CompletableFuture<AssumeRoleWithWebIdentityResponse> requestCredentials(String token) {
AssumeRoleWithWebIdentityRequest request = AssumeRoleWithWebIdentityRequest.builder()
.roleArn(roleArn)
.roleSessionName(roleSessionName)
.webIdentityToken(token)
.build();
return stsAsyncClient.assumeRoleWithWebIdentity(request);
}
private Cached toCached(AssumeRoleWithWebIdentityResponse response) {
Credentials credentials = response.credentials();
if (credentials == null) {
throw new IllegalStateException("STS AssumeRoleWithWebIdentity response did not include credentials");
}
Instant expiry = credentials.expiration();
if (expiry == null) {
throw new IllegalStateException("STS AssumeRoleWithWebIdentity response did not include a credential expiry");
}
Instant now = clock.instant();
if (expiry.isAfter(now) == false) {
throw new IllegalStateException("STS returned credentials that are already expired at [" + expiry + "] (now [" + now + "])");
}
AwsSessionCredentials sessionCredentials = AwsSessionCredentials.builder()
.accessKeyId(credentials.accessKeyId())
.secretAccessKey(credentials.secretAccessKey())
.sessionToken(credentials.sessionToken())
.expirationTime(expiry)
.build();
return new Cached(sessionCredentials, expiry.minus(prefetchTime), expiry.minus(staleTime));
}
private static Throwable unwrap(Throwable t) {
return t instanceof CompletionException && t.getCause() != null ? t.getCause() : t;
}
View on GitHub (pinned to db6a809a66)
Solutions
- Ensure the Credentials response includes expiration (for tests: Credentials.builder().expiration(Instant.now().plusSeconds(900)))
- If hitting a real STS-compatible endpoint, confirm it populates the Expiration field per the AWS schema
- Upgrade/align the AWS SDK version so the field deserialises correctly
Example fix
// before
Credentials c = Credentials.builder().accessKeyId("k").secretAccessKey("s").build();
// after
Credentials c = Credentials.builder().accessKeyId("k").secretAccessKey("s").expiration(Instant.now().plusSeconds(900)).build(); Defensive patterns
Strategy: validation
Validate before calling
Credentials c = resp.credentials();
if (c == null || c.expiration() == null) {
throw new IllegalStateException("STS credentials missing expiry");
} Type guard
static boolean hasExpiry(Credentials c) {
return c != null && c.expiration() != null;
} Try / catch
try { toCached(resp); }
catch (IllegalStateException e) { /* log + retry */ } Prevention
- Always set expiration in test stubs
- Verify STS-compatible endpoints populate Expiration
- Pin the AWS SDK version
When it happens
Trigger: STS returns a Credentials object whose expiration() is null. Reached only after the null-credentials check passed.
Common situations: Mocked or stubbed credentials without an expiration; an STS-compatible service that omits the expiration field; SDK version skew where the field is named differently; misconfigured local STS emulator.
Related errors
- STS AssumeRoleWithWebIdentity response did not include crede
- STS returned credentials that are already expired at [${expi
- staleTime must be a positive duration but was [${staleTime}]
- prefetchTime must be a positive duration but was [${prefetch
- prefetchTime [${prefetchTime}] must be greater than or equal
AI-assisted analysis of elastic/elasticsearch@db6a809a66 (2026-08-12).
Data as JSON: /api/errors/139ae5b2e27fd4a0.
Report an issue: GitHub.