fullstackhero/dotnet-starter-kit · error · ForbiddenException
no policy
Error message
no policy
What it means
After loading the file, the handler asks FileAccessPolicyRegistry.Resolve(f.OwnerType) for a per-owner-type access policy; when the registry has no policy registered for that OwnerType it throws ForbiddenException("no policy"). This is a configuration/registration failure, not a user-permission problem: a new OwnerType was introduced without an accompanying IFileAccessPolicy implementation.
Solutions
- Register an IFileAccessPolicy for the missing OwnerType in DI so FileAccessPolicyRegistry can resolve it.
- Log/inspect the actual f.OwnerType value on the offending FileAssets row and compare it against the registered policy keys.
- Add a startup guard or unit test that asserts every valid OwnerType enum/string value has a registered policy.
- If the row's OwnerType is corrupt data, fix the row or delete it via a data migration.
Example fix
// before
// no registration for OwnerType "Project" -> policies.Resolve("Project") returns null
// after
services.AddSingleton<IFileAccessPolicy, ProjectFileAccessPolicy>(); // keyed/resolved by OwnerType "Project" Defensive patterns
Strategy: validation
Validate before calling
var ownerTypes = new[] { "User", "Organization" /* all valid values */ };
if (!ownerTypes.Contains(asset.OwnerType)) throw new InvalidOperationException($"No access policy registered for OwnerType '{asset.OwnerType}'."); Type guard
bool HasPolicy(FileAccessPolicyRegistry registry, string ownerType) => registry.Resolve(ownerType) is not null;
Try / catch
catch (ForbiddenException e) when (e.Message == "no policy") {
logger.LogError(e, "Unregistered OwnerType on file {FileId}", fileId);
return Results.Problem("Server misconfiguration: file access policy missing.", statusCode: 500);
} Prevention
- Register an IFileAccessPolicy for every new OwnerType at the same time you introduce it.
- Add a startup assertion covering all OwnerType values against the registry.
- Never hand-edit OwnerType in seed/import data without a matching policy.
- Cover the registry with a unit test enumerating valid owner types.
When it happens
Trigger: FileAssets row whose OwnerType has no IFileAccessPolicy registered in FileAccessPolicyRegistry (missing DI registration, policy assembly not scanned, typo in OwnerType string, or a new owner kind added without a policy).
Common situations: Developer adds a new owner type (e.g. new module owning files) and forgets to register its policy; DI container rebuilt without the policy's module; environment-specific registration omitted; OwnerType values edited in seed data without matching policies.
Related errors
- no policy
- not allowed to change this file's visibility
- not allowed to delete this file
- invalid tenant
- Unknown category ' '.
AI-assisted analysis of fullstackhero/dotnet-starter-kit@3f2959e683 (2026-09-15).
Data as JSON: /api/errors/5ec1615cc6d9e8d0.
Report an issue: GitHub.
Appendix: source
Thrown at src/Modules/Files/Modules.Files/Features/v1/ChangeVisibility/ChangeFileVisibilityCommandHandler.cs:42
{
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");
}
f.ChangeVisibility(cmd.Visibility);
await db.SaveChangesAsync(cancellationToken).ConfigureAwait(false);
var publicUrl = f.Visibility == Visibility.Public
? storage.BuildPublicUrl(f.StorageKey)
: null;
return FileAssetMapper.ToDto(f, publicUrl);
}
}
View on GitHub (pinned to 3f2959e683)