Plugin Service Resolution in FabrCore
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:
AgentGrain(which implementsIFabrCoreAgentHost) creates theFabrCoreAgentProxyand passesthisas a non-nullable host parameter- When the proxy calls
ResolveConfiguredToolsAsync(), it passes the host toFabrCoreToolRegistry.ResolveToolsAsync() - The registry wraps the service provider in a
PluginServiceProviderthat returns the host whentypeof(IFabrCoreAgentHost)is requested - 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:
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 toInitializeAsync, not a provider stored elsewhere - Older SDK version — The
PluginServiceProviderwrapper 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:
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.
Builder of FabrCore and OpenCaddis.