apereo/cas · error · IllegalArgumentException
Multiple S3 objects where found in bucket
Error message
Multiple S3 objects where found in bucket
What it means
AmazonS3SamlIdPMetadataLocator.fetchInternal() lists objects in the configured S3 bucket expecting exactly one metadata object. If more than one object is found it logs a warning, deletes all located objects, and throws IllegalArgumentException that multiple S3 objects were found in the bucket.
Solutions
- Empty the bucket (or the configured key prefix) so exactly one metadata object remains, then regenerate the IdP metadata once.
- Give each registered service/IdP its own bucket or prefix so locator queries cannot match multiple objects.
- Audit what wrote the extra objects (repeated sso/idp metadata generation) and disable duplicate writers.
- Re-run metadata generation after cleanup; note this locator deletes the conflicting objects before throwing.
Example fix
// before (shared bucket with leftovers) cas.authn.samlIdp.metadata.amazon-s3.bucket-name=cas-saml-metadata // contains idp-metadata.xml, idp-metadata(1).xml // after aws s3 rm s3://cas-saml-metadata --exclude "*" --include "*.xml" # then regenerate metadata once; or dedicate bucket per service cas.authn.samlIdp.metadata.amazon-s3.bucket-name=cas-saml-metadata-sp1
Defensive patterns
Strategy: validation
Validate before calling
// before configuring the S3 locator, verify bucket holds exactly one metadata object
var listing = s3.listObjectsV2(ListObjectsV2Request.builder().bucket(bucket).build());
if (listing.contents().size() != 1)
throw new IllegalStateException("Bucket " + bucket + " must contain exactly 1 object, has " + listing.contents().size()); Try / catch
try {
doc = locator.fetch();
} catch (IllegalArgumentException e) {
logger.error("S3 bucket cleanup required: {}", e.getMessage());
} Prevention
- Dedicate one bucket or key prefix per metadata artifact.
- Enable S3 versioning instead of accumulating sibling objects.
- Monitor the bucket object count and alert when it exceeds one.
When it happens
Trigger: fetchInternal() (public) calls listObjectsV2 on bucketToUse and the result contains 2+ keys — e.g. metadata written multiple times without cleanup, or the bucket is shared with other content.
Common situations: Reusing one bucket for multiple IdP services/versions; a failed previous upload left stale objects; misconfigured bucket/prefix so unrelated objects are matched; concurrent writers.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- No assertion consumer service could be found for entity
- Endpoint for is not available or does not define a binding…
- Endpoint for does not define a binding or location for…
- Metadata directory location cannot be located/created
- Metadata directory location cannot be located/created
AI-assisted analysis of apereo/cas@e7288fc434 (2026-09-08).
Data as JSON: /api/errors/2696d3bf709d2d0b.
Report an issue: GitHub.
Appendix: source
Thrown at support/cas-server-support-saml-idp-metadata-aws-s3/src/main/java/org/apereo/cas/support/saml/idp/metadata/AmazonS3SamlIdPMetadataLocator.java:66
LOGGER.debug("S3 bucket [{}] does not exist", bucketToUse);
return null;
}
val result = s3Client.listObjectsV2(ListObjectsV2Request.builder().bucket(bucketToUse).build());
val objects = result.contents();
LOGGER.debug("Located [{}] S3 object(s) from bucket [{}]", objects.size(), bucketToUse);
if (objects.isEmpty()) {
return null;
}
if (objects.size() > 1) {
LOGGER.warn("Located [{}] S3 object(s) from bucket [{}]", objects.size(), bucketToUse);
objects.forEach(obj -> {
LOGGER.debug("Deleting object [{}] from bucket [{}]", obj.key(), bucketToUse);
val deleteRequest = DeleteObjectRequest.builder().bucket(bucketToUse).key(obj.key()).build();
s3Client.deleteObject(deleteRequest);
});
throw new IllegalArgumentException("Multiple S3 objects where found in bucket " + bucketToUse);
}
val firstMetadataObject = objects.getFirst();
LOGGER.debug("Fetching object [{}] from bucket [{}]", firstMetadataObject.key(), bucketToUse);
val metadataEntry = s3Client.getObject(GetObjectRequest.builder().bucket(bucketToUse).key(firstMetadataObject.key()).build());
return AmazonS3SamlIdPMetadataUtils.readMetadataDocumentFromBucket(metadataEntry, bucketToUse);
}
}
View on GitHub (pinned to e7288fc434)