fullstackhero/dotnet-starter-kit · error · CustomException
Extension ' ' not allowed for category ' '.
Error message
Extension '{extension}' not allowed for category '{cmd.Category}'. What it means
CustomException (BadRequest) thrown when the uploaded file's extension (from Path.GetExtension on cmd.FileName) is empty or not in the category's AllowedExtensions list (case-insensitive). The gate exists to keep unapproved file types out of storage before a presigned URL is minted.
Solutions
- Send a file whose extension is in the target category's AllowedExtensions config, or convert the file to an allowed type.
- If the type is legitimately needed, add that extension to the category's AllowedExtensions in configuration.
- Update the client file-picker accept filter to match the server's allow-list so users cannot select disallowed files.
- Validate extension client-side before calling the API to give instant feedback.
Example fix
// before
await requestUploadUrl({ category: "avatar", fileName: "photo.bmp", ... }); // .bmp not allowed
// after
await requestUploadUrl({ category: "avatar", fileName: "photo.png", ... }); // .png in AllowedExtensions Defensive patterns
Strategy: validation
Validate before calling
const ext = fileName.slice(fileName.lastIndexOf(".")).toLowerCase();
if (!category.allowedExtensions.map(e => e.toLowerCase()).includes(ext)) throw new Error(`.${ext} not allowed`); Try / catch
try { await requestUploadUrl(cmd); } catch (e) { if (e.status === 400 && e.message?.includes("not allowed for category")) { showToast("File type not supported for this upload"); } else throw e; } Prevention
- Set the file input's accept attribute from the same allow-list the server uses.
- Normalize filenames (trim, lowercase extension) before submitting.
- Keep client-side allow-lists and server config in one shared source of truth.
When it happens
Trigger: RequestUploadUrlCommand with cmd.FileName having no extension ('README'), a double extension not listed ('file.tar.gz' when only '.gz' allowed), a blocked type ('.exe' for the avatar category), or an extension present only in a different category's allow-list.
Common situations: Frontend letting users pick files without filtering by accept attribute; renaming files to hide types; configuration allow-list tightened without updating client UI; case handled fine (comparison is OrdinalIgnoreCase) but hidden characters or trailing dots in the filename cause mismatch.
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
- Declared size must be positive.
- Unknown visibility value
- uploaded size ( ) exceeds declared ( )
- uploaded content-type mismatch
- Unknown category ' '.
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/f75f8ac70585a9b1.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Files/Modules.Files/Features/v1/RequestUploadUrl/RequestUploadUrlCommandHandler.cs:48
var tenantId = currentUser.GetTenant() ?? throw new UnauthorizedException("invalid tenant");
var userId = currentUser.GetUserId();
if (userId == Guid.Empty)
{
throw new UnauthorizedException("no current user");
}
// Category lookup + extension/size validation.
if (!options.Value.Categories.TryGetValue(cmd.Category, out var category))
{
throw new CustomException($"Unknown category '{cmd.Category}'.", (IEnumerable<string>?)null, HttpStatusCode.BadRequest);
}
var extension = Path.GetExtension(cmd.FileName);
if (string.IsNullOrWhiteSpace(extension) ||
!category.AllowedExtensions.Contains(extension, StringComparer.OrdinalIgnoreCase))
{
throw new CustomException(
$"Extension '{extension}' not allowed for category '{cmd.Category}'.",
(IEnumerable<string>?)null,
HttpStatusCode.BadRequest);
}
if (cmd.SizeBytes > category.MaxBytes)
{
throw new CustomException(
$"File exceeds max size of {category.MaxBytes} bytes for category '{cmd.Category}'.",
(IEnumerable<string>?)null,
HttpStatusCode.BadRequest);
}
// Authorization: policy must exist and allow the attach.
var policy = policies.Resolve(cmd.OwnerType)
?? throw new ForbiddenException($"No file access policy registered for owner type '{cmd.OwnerType}'.");
if (!await policy.CanAttachAsync(cmd.OwnerId, userId.ToString(), cancellationToken).ConfigureAwait(false))
{View on GitHub (pinned to 3f2959e683)