abpframework/abp · error · AbpException

Options must be derived from the {typeof(AbpDynamicOptionsMa

Error message

Options must be derived from the {typeof(AbpDynamicOptionsManager<>).FullName}!

What it means

Thrown by OptionsAbpDynamicOptionsManagerExtensions.ToDynamicOptions when SetAsync is called on an IOptions<T> instance that is not an AbpDynamicOptionsManager<T>. ABP's dynamic-configuration feature overrides the default OptionsManager so option values can be refreshed at runtime; calling SetAsync on a plain/externally-supplied IOptions has no dynamic backing store to update.

Source

Thrown at framework/src/Volo.Abp.Core/Microsoft/Extensions/Options/OptionsAbpDynamicOptionsManagerExtensions.cs:29

    {
        return options.ToDynamicOptions().SetAsync();
    }

    public static Task SetAsync<T>(this IOptions<T> options, string name)
        where T : class
    {
        return options.ToDynamicOptions().SetAsync(name);
    }

    private static AbpDynamicOptionsManager<T> ToDynamicOptions<T>(this IOptions<T> options)
        where T : class
    {
        if (options is AbpDynamicOptionsManager<T> dynamicOptionsManager)
        {
            return dynamicOptionsManager;
        }

        throw new AbpException($"Options must be derived from the {typeof(AbpDynamicOptionsManager<>).FullName}!");
    }
}

View on GitHub (pinned to 7ed43b1931)

Solutions

  1. Ensure the AbpOptions module / dynamic-configuration infrastructure is loaded so IOptions<T> resolves as AbpDynamicOptionsManager<T> at runtime.
  2. In tests, do not call SetAsync on Options.Create; instead register dynamic options or skip the SetAsync call.
  3. If you only need static options, read .Value instead of calling SetAsync.
  4. Confirm AbpDynamicOptionsManager is the registered IOptions<T> implementation (check that AddAbp/your module wired IOptions via the ABP options services).

Example fix

// before — Options.Create is not dynamic
var opts = Options.Create(new MyOptions { X = 1 });
await opts.SetAsync(); // throws

// after — register through ABP dynamic options, or just read the value
var value = opts.Value;
// or configure dynamic options via Configure<T>/ ABP's SetConfigurationValue path
Defensive patterns

Strategy: type-guard

Validate before calling

// Only call SetAsync when the resolved options are ABP's dynamic manager
if (options is Volo.Abp.Options.AbpDynamicOptionsManager<T> dynamicOpts)
    await dynamicOpts.SetAsync();
else
    throw new InvalidOperationException("IOptions<T> is not the ABP dynamic manager; cannot SetAsync.");

Type guard

static bool IsDynamic<T>(IOptions<T> options) where T : class =>
    options is Volo.Abp.Options.AbpDynamicOptionsManager<T>;

Prevention

When it happens

Trigger: Invoking options.SetAsync() / options.SetAsync(name) where options is a standard OptionsManager<T>, a wrapped IOptions<T> from a non-ABP source, or a test double — anything that is not AbpDynamicOptionsManager<T>.

Common situations: Unit tests injecting a plain IOptions<T> (e.g. Options.Create(value)) and then calling SetAsync; code that resolves IOptions<T> from a container not configured with ABP's dynamic options; using Microsoft.Extensions.Options directly without the ABP options module.

Related errors


AI-assisted analysis of abpframework/abp@7ed43b1931 (2026-08-13). Data as JSON: /api/errors/6ac5b23870a6c1db. Report an issue: GitHub.