Skip to content
Written with Claude

Changelog v3.15.2 (2026-05-11)

Version 3.15.2 (2026-05-11)

Full Changelog

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
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 when Policies was treated as an opaque dictionary.
  • Cross-type keys are caught: e.g. TokensPerPeriod placed on a FixedWindow policy is flagged, since it belongs to TokenBucket.

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, When

Each 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, StatusCode

ValidationOptions: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 in FindUnknownConfigKeys, three new schema helpers, ValidationOptions:Rules removed from IsOpenDictionarySection.
  • 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).

Comments