fullstackhero/dotnet-starter-kit · warning · CustomException
Unknown visibility value
Error message
Unknown visibility value '{cmd.Visibility}'. What it means
ChangeFileVisibilityCommandHandler validates that cmd.Visibility is one of the known enum values (Public or Private) and throws CustomException with 400 BadRequest otherwise. Because the value is likely bound from a string in the request body, an unrecognized string arrives as an out-of-range/undefined enum value rather than failing model binding.
Solutions
- Send exactly "Public" or "Private" (match the enum's serialized casing).
- Constrain the UI to a dropdown/typed union of allowed values instead of free text.
- Check the API version — a value valid in an older version may have been removed.
- Add client-side zod validation restricting visibility to the allowed literals before sending.
Example fix
// before
await apiFetch(`/files/${id}/visibility`, { method: 'PATCH', body: JSON.stringify({ visibility: 'public' }) }); // 400
// after
const allowed = ['Public', 'Private'] as const;
if (!allowed.includes(next as any)) throw new Error('invalid visibility');
await apiFetch(`/files/${id}/visibility`, { method: 'PATCH', body: JSON.stringify({ visibility: next }) }); Defensive patterns
Strategy: validation
Validate before calling
const allowed = ['Public', 'Private'] as const; type Visibility = (typeof allowed)[number]; const isVisibility = (v: string): v is Visibility => (allowed as readonly string[]).includes(v);
Type guard
const isVisibility = (v: unknown): v is 'Public' | 'Private' => v === 'Public' || v === 'Private';
Prevention
- Use a typed union/dropdown instead of free-text input for visibility
- Add zod schema with enum validation before sending
- Keep client enum literals in sync with the backend contract
When it happens
Trigger: PATCHing visibility with a typo'd or unsupported string (e.g. "public" wrong case if JSON deserialization is case-sensitive, "internal", "shared", "") that does not map to Public or Private.
Common situations: Older client versions sending removed enum values after an API change; hand-written API clients guessing values; tests sending raw strings not in the enum; numeric enum values outside the defined range.
Understand the failure class
Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.
Related errors
- A category cannot be its own parent.
- Declared size must be positive.
- 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/7888d916f69b4e55.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Files/Modules.Files/Features/v1/ChangeVisibility/ChangeFileVisibilityCommandHandler.cs:29
using Mediator;
using Microsoft.EntityFrameworkCore;
namespace FSH.Modules.Files.Features.v1.ChangeVisibility;
public sealed class ChangeFileVisibilityCommandHandler(
FilesDbContext db,
FileAccessPolicyRegistry policies,
ICurrentUser currentUser,
IStorageService storage)
: ICommandHandler<ChangeFileVisibilityCommand, FileAssetDto>
{
public async ValueTask<FileAssetDto> Handle(ChangeFileVisibilityCommand cmd, CancellationToken cancellationToken)
{
ArgumentNullException.ThrowIfNull(cmd);
if (cmd.Visibility is not (Visibility.Public or Visibility.Private))
{
throw new CustomException(
$"Unknown visibility value '{cmd.Visibility}'.",
errors: null,
System.Net.HttpStatusCode.BadRequest);
}
var f = await db.FileAssets
.FirstOrDefaultAsync(x => x.Id == cmd.FileAssetId, cancellationToken)
.ConfigureAwait(false)
?? throw new NotFoundException("file not found");
var userId = currentUser.GetUserId().ToString();
var policy = policies.Resolve(f.OwnerType)
?? throw new ForbiddenException("no policy");
var ctx = new FileAccessContext(f.Id, f.OwnerType, f.OwnerId, f.CreatedByUserId, (int)f.Visibility);
if (!await policy.CanChangeVisibilityAsync(ctx, userId, cancellationToken).ConfigureAwait(false))
{
throw new ForbiddenException("not allowed to change this file's visibility");
}View on GitHub (pinned to 3f2959e683)