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

  1. Register an IFileAccessPolicy for the missing OwnerType in DI so FileAccessPolicyRegistry can resolve it.
  2. Log/inspect the actual f.OwnerType value on the offending FileAssets row and compare it against the registered policy keys.
  3. Add a startup guard or unit test that asserts every valid OwnerType enum/string value has a registered policy.
  4. 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

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


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)