RayWangQvQ/BiliBiliToolPro · critical · InvalidOperationException

Unable to get SqliteConfigurationProvider

Error message

Unable to get SqliteConfigurationProvider

What it means

HandleValidSubmitAsync in the Blazor config page saves settings through the SqliteConfigurationProvider obtained from the app's IConfigurationRoot. If the provider cannot be resolved (GetSqliteConfigurationProvider() returns null) it throws InvalidOperationException('Unable to get SqliteConfigurationProvider'), meaning the in-memory configuration does not contain the SQLite config source that must back persisted settings.

Solutions

  1. Ensure startup adds the SQLite configuration source (the AddSqliteConfig/extension used by the default Program.cs) before the Blazor app runs.
  2. Verify the SQLite database file path exists and the process has write permission to it.
  3. Check GetSqliteConfigurationProvider(): iterate configurationRoot.Providers and confirm a SqliteConfigurationProvider is present; fix the detection logic if the type changed after a refactor.
  4. Run with the stock Program.cs/host builder to confirm default behavior, then re-apply customizations.

Example fix

// before
var sqliteProvider = GetSqliteConfigurationProvider();
if (sqliteProvider == null)
    throw new InvalidOperationException("Unable to get SqliteConfigurationProvider");
// after (log available providers to diagnose, fail with context)
var providers = ((IConfigurationRoot)Configuration).Providers.Select(p => p.GetType().Name);
logger.LogError("No SqliteConfigurationProvider. Loaded: {Providers}", string.Join(",", providers));
throw new InvalidOperationException($"Unable to get SqliteConfigurationProvider. Loaded: [{string.Join(\",\", providers)}]");
Defensive patterns

Strategy: validation

Validate before calling

var root = (IConfigurationRoot)Configuration;
bool hasSqlite = root.Providers.Any(p => p is SqliteConfigurationProvider);
if (!hasSqlite)
    logger.LogError("SQLite配置源未注册,无法保存配置");

Try / catch

try { await HandleValidSubmitAsync(); }
catch (InvalidOperationException ex)
{
    message = $"保存失败:{ex.Message}。请确认应用以默认启动方式运行(包含SQLite配置源)";
}

Prevention

When it happens

Trigger: The host was built without the SQLite configuration source being added (e.g. AddSqliteConfig not invoked or DB path misconfigured), the IConfigurationRoot has no provider whose type is SqliteConfigurationProvider, or the page is rendered in an environment (test host, alternate startup) that constructs configuration differently.

Common situations: Running the Web UI with a custom Program.cs that omits the SQLite config registration; DB file path unwritable so provider initialization silently failed; upgrading the app with a startup refactor that dropped the provider.

Related errors


AI-assisted analysis of RayWangQvQ/BiliBiliToolPro@c599b2c0da (2026-09-12). Data as JSON: /api/errors/81924265abc5bb14. Report an issue: GitHub.

Appendix: source

Thrown at src/Ray.BiliBiliTool.Web/Components/Pages/Configs/BaseConfigComponent.cs:81

            _isLoading = false;
            StateHasChanged();
        }

        return Task.CompletedTask;
    }

    protected virtual async Task HandleValidSubmitAsync()
    {
        _isLoading = true;
        _saveMessage = null;

        try
        {
            // 保存配置
            var sqliteProvider = GetSqliteConfigurationProvider();
            if (sqliteProvider == null)
            {
                throw new InvalidOperationException("Unable to get SqliteConfigurationProvider");
            }

            var configValues = _config.ToConfigDictionary();
            sqliteProvider.BatchSet(configValues);

            // 如果有对应的定时任务,同步更新 Quartz 任务状态和 Cron 表达式
            var jobKey = GetJobKey();
            if (jobKey != null && SchedulerService != null)
            {
                // 更新 Cron 表达式
                await UpdateJobCronAsync(jobKey, _config.Cron);

                // 控制任务启停
                await ControlScheduledJobAsyc(jobKey, _config.IsEnable);
            }

            _saveMessage = GetSaveSuccessMessage();
            _saveSuccess = true;

View on GitHub (pinned to c599b2c0da)