Plugin Service Resolution in FabrCore

Eric Brasher February 21, 2026 at 9:05 AM 4 min read

A common question from teams building FabrCore plugins is whether IFabrCoreAgentHost is available during InitializeAsync. Short answer: yes, it is — and if it's resolving to null, the issue is almost certainly in how you're resolving it.

How Service Resolution Works

When FabrCore initializes a plugin, it wraps the standard IServiceProvider in a PluginServiceProvider that injects additional services. Here's the chain:

  1. AgentGrain (which implements IFabrCoreAgentHost) creates the FabrCoreAgentProxy and passes this as a non-nullable host parameter
  2. When the proxy calls ResolveConfiguredToolsAsync(), it passes the host to FabrCoreToolRegistry.ResolveToolsAsync()
  3. The registry wraps the service provider in a PluginServiceProvider that returns the host when typeof(IFabrCoreAgentHost) is requested
  4. This wrapped provider is passed to both constructor injection and InitializeAsync

So serviceProvider.GetService<IFabrCoreAgentHost>() inside InitializeAsync should return a non-null value when the plugin is loaded through the standard flow.

Correct Usage

Always use GetRequiredService on the IServiceProvider parameter passed directly to InitializeAsync:

MyPlugin.cs
public Task InitializeAsync(
    AgentConfiguration config,
    IServiceProvider serviceProvider)
{
    // Use GetRequiredService — throws immediately if unavailable
    var host = serviceProvider.GetRequiredService<IFabrCoreAgentHost>();

    // host is guaranteed non-null through the standard flow
    return Task.CompletedTask;
}

Using GetRequiredService instead of GetService surfaces the real problem immediately rather than silently degrading with null checks throughout your plugin.

Common Pitfalls

If IFabrCoreAgentHost resolves to null, check for these causes:

  • Using a cached or different IServiceProvider — Always use the parameter passed to InitializeAsync, not a provider stored elsewhere
  • Older SDK version — The PluginServiceProvider wrapper was added in a recent release. Ensure you're on the latest SDK
  • Resolving outside the standard flow — Plugins created outside FabrCoreAgentProxy (e.g., in unit tests or custom initialization) won't have the wrapped provider

Optional: Base Class Pattern

If you have many plugins with similar boilerplate, consider an abstract base class:

FabrCorePluginBase.cs
public abstract class FabrCorePluginBase : IFabrCorePlugin
{
    protected IFabrCoreAgentHost AgentHost { get; private set; } = null!;
    protected AgentConfiguration Config { get; private set; } = null!;

    public Task InitializeAsync(
        AgentConfiguration config,
        IServiceProvider serviceProvider)
    {
        Config = config;
        AgentHost = serviceProvider
            .GetRequiredService<IFabrCoreAgentHost>();
        return OnInitializeAsync(config, serviceProvider);
    }

    protected virtual Task OnInitializeAsync(
        AgentConfiguration config,
        IServiceProvider sp) => Task.CompletedTask;
}

This eliminates nullable fields and defensive null-checks across all your plugins while keeping the core IFabrCorePlugin interface stable.

Learn More

Check out the plugin lifecycle documentation for the complete reference on services available during initialization.


Eric Brasher

Builder of FabrCore and OpenCaddis.