OrchardCMS/OrchardCore · error · FileStoreException
Error deleting file
Error message
Error deleting file '${path}': ${ex.Message} What it means
AwsFileStorage.TryDeleteFileAsync wraps unexpected AmazonS3Exception failures in FileStoreException. A 404 is treated as 'file already gone' (returns false); any other S3 failure — permissions, throttling, connectivity — throws this message naming the path, with the original exception as InnerException. It is also reachable indirectly via MoveFileAsync when the target deletion fails.
Solutions
- Grant s3:DeleteObject on the bucket/prefix to the configured IAM credentials
- Verify bucket/region/credentials in the Amazon S3 media settings and test delete manually
- Check the InnerException's ErrorCode for the precise AWS cause
- Add retry/backoff for throttling errors before surfacing the failure
Example fix
// before
await fileStore.MoveFileAsync(src, dst); // delete of src failed inside
// after
try { await fileStore.MoveFileAsync(src, dst); }
catch (FileStoreException ex) { _logger.LogError(ex.InnerException, "Delete failed for {Path}: {Code}", src, (ex.InnerException as AmazonS3Exception)?.ErrorCode); } Defensive patterns
Strategy: try-catch
Validate before calling
// pre-flight delete permission check:
await _s3.PutObjectAsync(new PutObjectRequest { BucketName = bucket, Key = probeKey, ContentBody = "probe" });
await _s3.DeleteObjectAsync(new DeleteObjectRequest { BucketName = bucket, Key = probeKey }); Type guard
bool IsAccessDenied(FileStoreException ex) => ex.InnerException is AmazonS3Exception s3 && s3.ErrorCode == "AccessDenied";
Try / catch
try { deleted = await fileStore.TryDeleteFileAsync(path); } catch (FileStoreException ex) when (ex.InnerException is AmazonS3Exception s3 && s3.StatusCode != HttpStatusCode.NotFound) { _logger.LogError(ex, "Delete failed for {Path}: {Code}", path, s3.ErrorCode); } Prevention
- Grant s3:DeleteObject on the bucket/prefix to the configured credentials
- Test MoveFileAsync paths — the delete inside it can throw too
- Enable retries for throttling during bulk deletes
- Monitor the inner AmazonS3Exception ErrorCode in logs
When it happens
Trigger: Calling TryDeleteFileAsync(path) (or MoveFileAsync which calls it) when DeleteObject fails with a non-404 AmazonS3Exception: AccessDenied, invalid credentials, throttling, or a misconfigured bucket.
Common situations: IAM policy lacking s3:DeleteObject on the prefix; read-only media credentials; S3 503 SlowDown during bulk deletes; bucket region mismatch after migration.
Understand the failure class
Background: "failed to write file", "Could not save figure", "Error saving remote file" — file write failed: causes and fixes across languages and libraries — this error's family across 38 libraries.
Related errors
- Error creating directory
- Error retrieving file info for
- Cannot delete root directory.
- Error accessing file
- Error while copying file
AI-assisted analysis of OrchardCMS/OrchardCore@4306c0717f (2026-09-13).
Data as JSON: /api/errors/d0e2d177fa411a5d.
Report an issue: GitHub.
Appendix: source
Thrown at src/OrchardCore/OrchardCore.FileStorage.AmazonS3/AwsFileStorage.cs:155
public async Task<bool> TryDeleteFileAsync(string path)
{
try
{
var response = await _amazonS3Client.DeleteObjectAsync(new DeleteObjectRequest
{
BucketName = _options.BucketName,
Key = this.Combine(_basePrefix, path),
});
return response.IsDeleteSuccessful();
}
catch (AmazonS3Exception ex) when (ex.StatusCode == HttpStatusCode.NotFound)
{
return false;
}
catch (AmazonS3Exception ex)
{
throw new FileStoreException($"Error deleting file '{path}': {ex.Message}", ex);
}
}
public async Task<bool> TryDeleteDirectoryAsync(string path)
{
if (string.IsNullOrWhiteSpace(path))
{
throw new FileStoreException("Cannot delete root directory.");
}
var listObjectsResponse = await _amazonS3Client.ListObjectsV2Async(new ListObjectsV2Request
{
BucketName = _options.BucketName,
Prefix = NormalizePrefix(this.Combine(_basePrefix, path)),
});
if (listObjectsResponse.S3Objects?.Count > 0)
{View on GitHub (pinned to 4306c0717f)