Troubleshooting
Practical problem/solution guide for Pragmatic.Configuration. Each section covers a common issue, the likely causes, and the fix.
Options Values Are All Default (Not Bound)
Section titled “Options Values Are All Default (Not Bound)”Your [Configuration] class compiles, but IOptions<T> returns default values instead of values from appsettings.json.
Checklist
Section titled “Checklist”-
Did you call the assembly aggregator? Verify that
services.Add{Prefix}Configuration(configuration)is called in yourIStartupStep:services.AddMyAppConfiguration(configuration); -
Does the section path match your JSON? The section is inferred by removing the
Optionssuffix.BookingOptionsexpects a"Booking"key inappsettings.json, not"BookingOptions". Check with[Configuration(SectionPath = "...")]if unsure. -
Is the JSON structure correct? The section must be a nested object, not a flat key:
// Correct{ "Booking": { "MaxGuests": 50 } }// Wrong -- flat key, not bound{ "Booking:MaxGuests": 50 } -
Is the
IConfigurationinstance the right one? Ensure you passconfiguration(fromIStartupSteporbuilder.Configuration), not a stale or empty configuration root. -
Are the property names cased correctly? Microsoft Configuration binding is case-insensitive, but the JSON property names must match after case normalization.
PRAG2000: Class Must Be Partial
Section titled “PRAG2000: Class Must Be Partial”Error: [Configuration] class 'BookingOptions' must be declared as partial
Fix: Add the partial keyword:
[Configuration]public partial class BookingOptions { ... }The SG cannot generate code for non-partial classes.
PRAG2001: Class Cannot Be Static or Abstract
Section titled “PRAG2001: Class Cannot Be Static or Abstract”Error: [Configuration] class 'BookingOptions' cannot be static or abstract
Fix: Remove static or abstract:
// Wrong[Configuration]public static class BookingOptions { ... }
// Right[Configuration]public partial class BookingOptions { ... }Options classes must be instantiable by the Microsoft Options framework.
PRAG2050: [Required] with Default Value
Section titled “PRAG2050: [Required] with Default Value”Warning: Property has [Required] but also has a default value
Fix: Decide the intent:
- If the property must be configured: keep
[Required], use empty/zero as the default. - If the property has a valid default: remove
[Required].
See Common Mistakes #9 for details.
Database Store: Schema Not Created
Section titled “Database Store: Schema Not Created”The database store throws SQL errors about missing tables.
Checklist
Section titled “Checklist”-
Is
AutoCreateSchemaenabled? Default istrue, but verify:services.AddDatabaseConfigurationStore(options =>{options.AutoCreateSchema = true; // default}); -
Does the connection have CREATE TABLE permissions? The schema manager creates tables with
CREATE TABLE IF NOT EXISTS(or equivalent per dialect). The database user must have DDL permissions. -
Is the correct provider set? Each provider has different DDL syntax.
DatabaseProvider.PostgreSqlgenerates PostgreSQL-specific SQL:options.Provider = DatabaseProvider.PostgreSql; // must match your database
Database Store: Secret Decryption Fails
Section titled “Database Store: Secret Decryption Fails”GetSecretAsync throws a cryptography exception.
Possible Causes
Section titled “Possible Causes”Wrong encryption key. If you changed the key after secrets were encrypted, existing secrets cannot be decrypted.
Truncated key. The key must be exactly 32 bytes (256 bits), base64-encoded. A shorter key will cause authentication failures.
Key from wrong source. DatabaseConfigurationOptions.EncryptionKey takes precedence over the PRAGMATIC_SECRET_KEY environment variable. If both are set with different values, you may be encrypting with one and decrypting with the other.
Fix: Verify the key is consistent across all instances of the application:
// Generate a new keyvar key = Convert.ToBase64String(RandomNumberGenerator.GetBytes(32));// key length should be 44 characters (32 bytes in base64)Hot-Reload Not Working
Section titled “Hot-Reload Not Working”Values change in the store but IOptionsMonitor<T> does not fire change callbacks.
Checklist
Section titled “Checklist”-
Are you using
IOptionsMonitor<T>?IOptions<T>does not support hot-reload. See Common Mistakes #8. -
Is the Pragmatic store bridged? You need
AddPragmaticStorein the configuration builder:builder.Configuration.AddPragmaticStore(store, environmentProfile); -
Is the store’s
WatchAsyncworking? The bridge subscribes toWatchAsync(). For the database backend, verifyEnableChangePollingistrueandPollingIntervalis reasonable. -
Check the Debug output.
PragmaticConfigurationProviderwrites reload failures toSystem.Diagnostics.Debug. Run in debug mode and check the output window.
Azure Backend: Authentication Fails
Section titled “Azure Backend: Authentication Fails”AddAzureAppConfigurationStore or AddAzureKeyVaultSecretStore throws authentication errors.
Checklist
Section titled “Checklist”-
Is
DefaultAzureCredentialconfigured? The Azure backend usesDefaultAzureCredential, which tries multiple credential sources in order: environment variables, managed identity, Azure CLI, Visual Studio, etc. -
For local development: Ensure you are logged in with
az loginand your account has access to the App Configuration / Key Vault resource. -
For production: Ensure the managed identity has the following roles:
- App Configuration:
App Configuration Data Reader(orData Ownerfor write) - Key Vault:
Key Vault Secrets User(orSecrets Officerfor write)
- App Configuration:
-
Connection string alternative: If managed identity is not available, use the connection string:
options.AppConfigurationConnectionString = "Endpoint=...;Id=...;Secret=...";
Tenant Override Not Applied
Section titled “Tenant Override Not Applied”You set a tenant-specific value but ConfigurationResolver returns the base value.
Checklist
Section titled “Checklist”-
Is multi-tenancy enabled?
services.AddPragmaticConfiguration(options =>{options.MultiTenant.Enabled = true;}); -
Is
ITenantContextresolved? TheConfigurationResolverreadsITenantContextfrom DI. Verify that Pragmatic.MultiTenancy is registered and the tenant is resolved for the current request. -
Is
FallbackToBasecausing confusion? Whentrue(default), the resolver falls back to the base value if no tenant override exists. This is correct behavior — but if you expected the tenant override to be applied and it is not, verify the value was set with the correct tenant ID.
Can I use [Configuration] without the runtime stores?
Section titled “Can I use [Configuration] without the runtime stores?”Yes. The source generator and runtime stores are independent. Use [Configuration] with standard appsettings.json and IOptions<T> — no AddPragmaticConfiguration() needed. The SG generates binding code that works with Microsoft’s built-in configuration system.
Can I use runtime stores without [Configuration]?
Section titled “Can I use runtime stores without [Configuration]?”Yes. Register AddPragmaticConfiguration() and use IConfigurationStore / ISecretStore directly. Or bridge to IConfiguration with AddPragmaticStore() and bind IOptions<T> manually.
How do I migrate from appsettings.json to a database store?
Section titled “How do I migrate from appsettings.json to a database store?”- Register the database store before
AddPragmaticConfiguration(). - Bridge with
AddPragmaticStore(). - Seed the database with values from
appsettings.json. - Both sources work simultaneously — Microsoft Configuration layers them, with later sources overriding earlier ones.
What happens if the database is down?
Section titled “What happens if the database is down?”The InMemoryConfigurationCacheStack caches values for the configured TTL. During the cache window, reads succeed even if the database is unreachable. After the cache expires, reads will fail. For critical configuration, ensure your cache TTLs are appropriate and consider fallback to appsettings.json as a secondary source.
Does the SG support nested options classes?
Section titled “Does the SG support nested options classes?”The SG binds to the section as a flat object. For nested configuration, use [Configuration(SectionPath = "Parent:Child")] or structure your appsettings.json to match the class hierarchy. Standard Microsoft Configuration binding supports nested objects natively.
Getting Help
Section titled “Getting Help”| Resource | Location |
|---|---|
| Concepts and architecture | concepts.md |
| Getting started | getting-started.md |
| Store backends | stores.md |
| Common mistakes | common-mistakes.md |
| Module README | ../README.md |