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

  1. Grant s3:DeleteObject on the bucket/prefix to the configured IAM credentials
  2. Verify bucket/region/credentials in the Amazon S3 media settings and test delete manually
  3. Check the InnerException's ErrorCode for the precise AWS cause
  4. 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

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


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)