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

  1. Empty the bucket (or the configured key prefix) so exactly one metadata object remains, then regenerate the IdP metadata once.
  2. Give each registered service/IdP its own bucket or prefix so locator queries cannot match multiple objects.
  3. Audit what wrote the extra objects (repeated sso/idp metadata generation) and disable duplicate writers.
  4. 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

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


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)