apache/iceberg · warning
Failed to add delete tags
Error message
Failed to add delete tags: {} to {} What it means
A warning from S3FileIO.deleteFile: the configured delete tags could not be applied to the object via tagFileToDelete (an S3Exception was raised), but the delete itself is NOT aborted — after logging, the code proceeds with the actual deletion unless deletion is disabled. The tags (often used for lifecycle-based soft delete or recovery, e.g. with the S3 Tables recovery feature) are simply missing on the object.
Solutions
- Grant the principal s3:PutObjectTagging (and s3:GetObjectTagging) on the bucket/objects in the IAM policy
- If you don't need delete tags, unset s3.delete.tags so the tagging step is skipped entirely
- Verify object ownership settings allow tagging from the writing account
- Note the file is still deleted after the warning; if tags were your safety net for recovery, consider enabling delete=false plus a lifecycle policy instead
Example fix
// before
{
"Effect": "Allow",
"Action": ["s3:DeleteObject"],
"Resource": "arn:aws:s3:::my-bucket/*"
}
// after
{
"Effect": "Allow",
"Action": ["s3:DeleteObject", "s3:PutObjectTagging", "s3:GetObjectTagging"],
"Resource": "arn:aws:s3:::my-bucket/*"
} Defensive patterns
Strategy: validation
Validate before calling
// verify tagging permission before deletes
try {
s3.getObjectTagging(b -> b.bucket(bucket).key(key));
} catch (S3Exception e) {
LOG.warn("Tagging not permitted on {}", key); // PutObjectTagging will fail too
} Prevention
- Grant s3:PutObjectTagging alongside s3:DeleteObject in IAM
- Only configure s3.delete.tags if tagging is actually permitted
- Watch for concurrent writers deleting objects before tagging
- Remember deletes proceed even when tagging fails — plan recovery accordingly
When it happens
Trigger: s3.delete.tags is configured and non-empty, but Object PutTagging fails — typically missing s3:PutObjectTagging permission, object already deleted by a concurrent writer, object versioning issues, or KMS/ownership restrictions.
Common situations: IAM policies granting s3:DeleteObject but not s3:PutObjectTagging; buckets with Object Ownership set to bucket-owner-enforced and cross-account tagging restrictions; expireSnapshot/delete operations where a concurrent compaction already removed the file.
Understand the failure class
Background: Permission denied / not authorized / 403 Forbidden: access-control rejections when the caller lacks the required role, grant, or ownership — this error's family across 18 libraries.
Related errors
- An error occurred while aborting the stream
- An error occurred while closing the stream
- Cannot commit because Glue cannot access the requested…
- Cannot create to generate and configure the client SDK…
- Cannot initialize S3FileIOAwsClientFactory, missing no-arg…
AI-assisted analysis of apache/iceberg@86d9c8fc54 (2026-09-12).
Data as JSON: /api/errors/f442ef19d234a8b3.
Report an issue: GitHub.
Appendix: source
Thrown at aws/src/main/java/org/apache/iceberg/aws/s3/S3FileIO.java:173
@Override
public InputFile newInputFile(String path, long length) {
return S3InputFile.fromLocation(path, length, clientForStoragePath(path), metrics);
}
@Override
public OutputFile newOutputFile(String path) {
return S3OutputFile.fromLocation(path, clientForStoragePath(path), metrics);
}
@Override
public void deleteFile(String path) {
PrefixedS3Client client = clientForStoragePath(path);
S3FileIOProperties s3FileIOProperties = client.s3FileIOProperties();
if (s3FileIOProperties.deleteTags() != null && !s3FileIOProperties.deleteTags().isEmpty()) {
try {
tagFileToDelete(client, path, s3FileIOProperties.deleteTags());
} catch (S3Exception e) {
LOG.warn("Failed to add delete tags: {} to {}", s3FileIOProperties.deleteTags(), path, e);
}
}
if (!s3FileIOProperties.isDeleteEnabled()) {
return;
}
S3URI location = new S3URI(path, s3FileIOProperties.bucketToAccessPointMapping());
DeleteObjectRequest deleteRequest =
DeleteObjectRequest.builder().bucket(location.bucket()).key(location.key()).build();
client.s3().deleteObject(deleteRequest);
}
@Override
public Map<String, String> properties() {
return properties.immutableMap();
}View on GitHub (pinned to 86d9c8fc54)