fullstackhero/dotnet-starter-kit · error · InvalidOperationException
File type ' ' is not allowed. Allowed
Error message
File type '{extension}' is not allowed. Allowed: {string.Join(", ", rules.AllowedExtensions)} What it means
S3StorageService.UploadAsync resolves FileTypeRules and rejects uploads whose FileName extension is missing or not in rules.AllowedExtensions, throwing InvalidOperationException before any S3 call. Identical policy to the local provider — file types are whitelisted per FileType for security.
Solutions
- Ensure the uploaded FileName has a whitelisted extension for the target FileType.
- Add the required extension to rules.AllowedExtensions in FileTypeMetadata if legitimate.
- Normalize file names on the client (trim, append correct extension) before upload.
- Pre-validate extensions in the frontend to fail fast.
Example fix
// before
await s3.UploadAsync(new UploadRequest { FileName = "document.v1", FileType = FileType.Document });
// after
await s3.UploadAsync(new UploadRequest { FileName = "document.pdf", FileType = FileType.Document }); // valid extension Defensive patterns
Strategy: validation
Validate before calling
var ext = Path.GetExtension(fileName);
var rules = FileTypeMetadata.GetRules(fileType);
if (string.IsNullOrWhiteSpace(ext) || !rules.AllowedExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase))
throw new ArgumentException($"Extension '{ext}' not allowed for {fileType}."); Type guard
bool IsAllowedExtension(string? ext, FileTypeRules rules) =>
!string.IsNullOrWhiteSpace(ext) && rules.AllowedExtensions.Contains(ext, StringComparer.OrdinalIgnoreCase); Try / catch
try {
await s3Storage.UploadAsync(request);
} catch (InvalidOperationException ex) when (ex.Message.StartsWith("File type")) {
logger.LogWarning("Rejected disallowed upload: {Message}", ex.Message);
return Results.BadRequest(ex.Message);
} Prevention
- Validate extensions at the API boundary before reaching the storage provider.
- Keep local and S3 providers on the same FileTypeMetadata rules so behavior doesn't drift.
- Reject root-less/extension-less names early in the upload endpoint.
When it happens
Trigger: UploadAsync against the S3 provider with a FileName lacking an extension or carrying an extension not whitelisted for that FileType.
Common situations: Switching from local to S3 storage with clients that previously bypassed validation; generated/synthetic files saved without extensions; extension casing or trailing whitespace; new file kinds added without updating FileTypeMetadata.
Understand the failure class
Background: "Must be a positive integer", "Invalid value", "Unsupported": the invalid-argument-value error family, when a library rejects the value you pass — this error's family across 35 libraries.
Related errors
- File type ' ' is not allowed. Allowed
- File exceeds max size of
- Storage quota exceeded
- File exceeds max size of
- upload not received
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/92d98b46cd801998.
Report an issue: GitHub.
Appendix: source
Thrown at src/BuildingBlocks/Storage/S3/S3StorageService.cs:51
_logger = logger;
_contentTypeProvider = new FileExtensionContentTypeProvider();
if (string.IsNullOrWhiteSpace(_options.Bucket))
{
throw new InvalidOperationException("Storage:S3:Bucket is required when using S3 storage.");
}
}
public async Task<string> UploadAsync<T>(FileUploadRequest request, FileType fileType, CancellationToken cancellationToken = default) where T : class
{
ArgumentNullException.ThrowIfNull(request);
var rules = FileTypeMetadata.GetRules(fileType);
var extension = Path.GetExtension(request.FileName);
if (string.IsNullOrWhiteSpace(extension) || !rules.AllowedExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
{
throw new InvalidOperationException($"File type '{extension}' is not allowed. Allowed: {string.Join(", ", rules.AllowedExtensions)}");
}
if (request.Data.Count > rules.MaxSizeInMB * 1024 * 1024)
{
throw new InvalidOperationException($"File exceeds max size of {rules.MaxSizeInMB} MB.");
}
var key = BuildKey<T>(SanitizeFileName(request.FileName));
using var stream = new MemoryStream([.. request.Data]);
var putRequest = new PutObjectRequest
{
BucketName = _options.Bucket,
Key = key,
InputStream = stream,
ContentType = request.ContentType
};View on GitHub (pinned to 3f2959e683)