RayWangQvQ/BiliBiliToolPro · critical · InvalidOperationException
IConfigurationRoot not available — cannot access Providers…
Error message
IConfigurationRoot not available — cannot access Providers or Reload()
What it means
BiliAccountPageWorkflow's primary constructor casts the injected IConfiguration to IConfigurationRoot and throws InvalidOperationException('IConfigurationRoot not available — cannot access Providers or Reload()') when the cast fails. The workflow needs IConfigurationRoot to enumerate providers and trigger Reload() after mutating account cookies; a plain IConfiguration wrapper (e.g. a custom implementation, Options-backed, or test double) cannot support that.
Solutions
- Register the real host configuration root in DI: services.AddSingleton<IConfiguration>(hostBuilder.Configuration) where hostBuilder.Configuration is the IConfigurationRoot.
- In tests, build a real ConfigurationRoot (new ConfigurationBuilder().AddInMemoryCollection(...) .Build()) instead of mocking IConfiguration.
- Ensure the service resolves the root, not a section — do not pass Configuration.GetSection(...).
- If a wrapper is unavoidable, implement IConfigurationRoot (expose Providers and Reload()) in the wrapper.
Example fix
// before (test)
services.AddSingleton<IConfiguration>(new ConfigurationBuilder().Build()); // not a Root? actually Build() returns Root — wrong case: custom wrapper
services.AddSingleton<IConfiguration>(new FakeConfiguration());
// after
var root = new ConfigurationBuilder().AddInMemoryCollection(new Dictionary<string,string>{["BiliBiliCookies__0"]="SESSDATA=x"}).Build();
services.AddSingleton<IConfiguration>(root); // root implements IConfigurationRoot Defensive patterns
Strategy: validation
Validate before calling
// before constructing the workflow
var root = configuration as IConfigurationRoot;
if (root is null)
throw new InvalidOperationException("DI必须注册真正的IConfigurationRoot(hostBuilder.Configuration)"); Try / catch
try { workflow = new BiliAccountPageWorkflow(configuration, loginSvc); }
catch (InvalidOperationException ex)
{
logger.LogError(ex, "BiliAccountPageWorkflow初始化失败:配置根不可用");
throw;
} Prevention
- Register hostBuilder.Configuration (the root) as IConfiguration in DI
- Never pass Configuration.GetSection(...) where the root is expected
- In tests build a real ConfigurationBuilder().Build() root instead of mocking IConfiguration
- Custom IConfiguration wrappers must implement IConfigurationRoot
When it happens
Trigger: DI registration supplies an IConfiguration implementation that is not the host's ConfigurationRoot (custom IConfiguration wrapper, configuration section passed instead of the root, or a test fake) so 'configuration as IConfigurationRoot' evaluates to null.
Common situations: Unit/integration tests registering a mock IConfiguration; refactored hosting code that registers a custom configuration implementation; injecting a configuration section rather than the root into the workflow.
Understand the failure class
Background: "is not a compatible type" / "cannot merge" errors: when a value's type doesn't match what the library requires — this error's family across 65 libraries.
Related errors
- Unable to get SqliteConfigurationProvider
- 获取青龙token失败
- 未配置白虎API Token
- SqliteConfigurationProvider not found
AI-assisted analysis of RayWangQvQ/BiliBiliToolPro@c599b2c0da (2026-09-12).
Data as JSON: /api/errors/331e5f88a0e99b8e.
Report an issue: GitHub.
Appendix: source
Thrown at src/Ray.BiliBiliTool.Web/Services/Pages/BiliAccount/BiliAccountPageWorkflow.cs:16
using Microsoft.Extensions.Configuration;
using Ray.BiliBiliTool.Agent;
using Ray.BiliBiliTool.Config.SQLite;
using Ray.BiliBiliTool.DomainService.Dtos;
using Ray.BiliBiliTool.DomainService.Interfaces;
namespace Ray.BiliBiliTool.Web.Services.Pages.BiliAccount;
public class BiliAccountPageWorkflow(
IConfiguration configuration,
ILoginDomainService loginDomainService
) : IBiliAccountPageWorkflow
{
private readonly IConfigurationRoot _configurationRoot =
configuration as IConfigurationRoot
?? throw new InvalidOperationException(
"IConfigurationRoot not available — cannot access Providers or Reload()"
);
public Task<List<BiliAccountDto>> GetAllAccountsAsync()
{
var cookieList = _configurationRoot.GetSection("BiliBiliCookies").Get<List<string>>() ?? [];
var accounts = new List<BiliAccountDto>();
for (int i = 0; i < cookieList.Count; i++)
{
var cookieStr = cookieList[i];
var userId = ParseUserId(cookieStr);
accounts.Add(new BiliAccountDto(i, userId, cookieStr));
}
return Task.FromResult(accounts);
}
View on GitHub (pinned to c599b2c0da)