Changelog v3.15.2 (2026-05-11)
Version 3.15.2 (2026-05-11)
Patch release finishing the config-validator fix started in 3.15.1. That release made Auth:Schemes validate by Type rather than by name; this one applies the same treatment to the two sister sections (RateLimiterOptions:Policies, CacheOptions:Profiles) that share the same "name-keyed open dictionary" shape, plus a small consistency win for ValidationOptions:Rules. Library version unchanged — fixes live entirely in NpgsqlRestClient/ConfigDefaults.cs.
Fix: RateLimiterOptions:Policies validates by Type, not by name
A configuration like
json
{
"Config": { "ValidateConfigKeys": "Error" },
"RateLimiterOptions": {
"Enabled": true,
"Policies": {
"login_throttle": {
"Type": "FixedWindow",
"PermitLimit": 10,
"WindowSeconds": 60,
"Partition": { "Sources": [{ "Type": "IpAddress" }] }
}
}
}
}failed startup with Unknown configuration key: RateLimiterOptions:Policies:login_throttle. The rate limiter itself registered login_throttle and rejected over-limit requests correctly — only the validator was wrong.
Root cause
FindUnknownConfigKeys walked the user's policy name (login_throttle) against the defaults schema, which contains illustrative example names (fixed, sliding, bucket, concurrency, per_user). Any other name was flagged unknown. With ValidateConfigKeys: "Error", that killed startup.
The 3.13.0 migration explicitly grouped RateLimiterOptions:Policies, CacheOptions:Profiles, and ValidationOptions:Rules as the same "object keyed by user-chosen name" shape, but only ValidationOptions:Rules was added to the validator's open-dictionary whitelist. The other two were missed.
What changed
FindUnknownConfigKeys now intercepts the descent at RateLimiterOptions:Policies:<name> and picks a per-Type schema:
- FixedWindow:
Type, Enabled, PermitLimit, WindowSeconds, QueueLimit, AutoReplenishment, Partition - SlidingWindow:
Type, Enabled, PermitLimit, WindowSeconds, SegmentsPerWindow, QueueLimit, AutoReplenishment, Partition - TokenBucket:
Type, Enabled, TokenLimit, TokensPerPeriod, ReplenishmentPeriodSeconds, QueueLimit, AutoReplenishment, Partition - Concurrency:
Type, Enabled, PermitLimit, QueueLimit, OldestFirst, Partition
The shared Partition sub-schema (Sources: [{ Type, Name, Value }], BypassAuthenticated) is appended to every type. When Type is missing/invalid the validator skips that policy silently, matching the runtime behavior in BuildRateLimiter.
Behavior after the fix
- Any custom policy name validates by its declared
Type; example names continue to validate as before. - Typos inside a policy (e.g.
PermitLimt) are caught — they were silently ignored whenPolicieswas treated as an opaque dictionary. - Cross-type keys are caught: e.g.
TokensPerPeriodplaced on aFixedWindowpolicy is flagged, since it belongs toTokenBucket.
Fix: CacheOptions:Profiles validates by shape
Same root cause, same shape of fix. Custom profile names (session_cache, api_responses, etc.) failed validation when ValidateConfigKeys: "Error" was set, because the defaults contain example names (fast_memory, shared_redis, date_range_hybrid).
All cache profiles share the same key set regardless of backend type (Memory / Redis / Hybrid) — only the backend selection varies — so a single flat schema covers every profile:
code
Enabled, Type, Expiration, Parameters, WhenEach When rule validates as { Parameter, Value, Then }. Typos inside a profile (e.g. Expirashun) are now caught.
Improvement: ValidationOptions:Rules now validates rule bodies
ValidationOptions:Rules was previously on the open-dictionary whitelist, so custom rule names (phone_number, etc.) passed validation — but typos inside a rule (e.g. Patrn instead of Pattern) also passed silently. All validation rules share the same flat key set regardless of Type (NotNull / NotEmpty / Required / Regex / MinLength / MaxLength):
code
Type, Pattern, MinLength, MaxLength, Message, StatusCodeValidationOptions:Rules has been removed from the whitelist and is now validated against this flat schema. Custom rule names still pass; typos inside any rule now surface.
Tests
NpgsqlRestTests/ConfigTests/ConfigValidationTests.cs gained 15 new tests covering all three sections: custom name acceptance, example-name regression coverage, typo flagging, cross-type key detection (rate limiter), Partition sub-block validation, When-rule sub-block validation, and missing-Type skip behavior for rate-limiter policies.
Files touched
NpgsqlRestClient/ConfigDefaults.cs— three new intercepts inFindUnknownConfigKeys, three new schema helpers,ValidationOptions:Rulesremoved fromIsOpenDictionarySection.NpgsqlRestTests/ConfigTests/ConfigValidationTests.cs— 15 new tests.
No changes to runtime config reading, no breaking changes, no library version bump (the bug was in NpgsqlRestClient only).