fix(binder): make the structural error codes configurable - #179
Merged
Conversation
RequestBindingError exposed no referenceable member: its two codes (REQUEST_ARGUMENT_REQUIRED / REQUEST_ARGUMENT_INVALID) were private and its factories internal, so a consumer could only branch on a string literal, and an API that prefixes its codes was stuck with two foreign ones the binder imposed. Carry the two codes on RequestBinderOptions (defaulting to the current values, exposed publicly as RequestBindingError.Default*Code) so a consumer overrides them once at the entry point; the binder helpers and NestedFailure source the configured code, so every structural failure -- scalars, list elements, and nested binders (which inherit the options) -- uses it. Factories stay internal: a consumer recognises these errors, it does not manufacture them. Refs: #147
Record the public-API-contract decision behind #147: the binder's two structural error codes are carried on RequestBinderOptions, defaulting to the current codes (exposed publicly), so a consumer overrides them once. Drafted as Proposed and indexed (EN + FR); the maintainer decides its status. The catalog-drift half is deferred (relates to #140). Refs: #147
Renumber ADR-0014 -> ADR-0015 (main took 0014 for the required-list presence contract, #177) and resolve the ADR index.
Renumber ADR-0015 -> ADR-0016 (main took 0015 for the Combine arity ceiling, #178) and resolve the ADR index.
Reefact
enabled auto-merge
July 18, 2026 11:22
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
The binder's two structural error codes —
REQUEST_ARGUMENT_REQUIREDandREQUEST_ARGUMENT_INVALID— wereprivatewithinternalfactories, soRequestBindingErrorexposed no member a consumer could reference: a consumer could only branch on a string literal, and an API that prefixes its codes was stuck with two foreign ones the binder imposed. This carries the two codes onRequestBinderOptions(defaulting to the current values, exposed publicly), so a consumer overrides them once at the entry point; every structural failure uses the configured code, and the factories stayinternal.Type of change
Changes
RequestBindingErrorfactories by anErrorCode, and expose the defaults publicly asRequestBindingError.DefaultArgumentRequiredCode/DefaultArgumentInvalidCode(so a zero-config consumer branches symbolically). The factories stayinternal— a consumer recognises these errors, it does not manufacture them.RequestBinderOptions(two optional constructor parameters +ArgumentRequiredCode/ArgumentInvalidCodeproperties), defaulting to the exposed defaults.RecordArgumentRequired/RecordArgumentInvalid, andNestedFailure.Group. Every path — scalars, list elements, and nested binders (which inherit the options) — uses it.StructuralCodeOverrideTests(8 tests): custom codes flow through missing/invalid scalars, list elements, a missing complex property, and a nested binder (both codes, prefixed paths); defaults unchanged; public defaults exposed; symbolic branching.Testing
dotnet build FirstClassErrors.sln— succeeded, 0 warnings / 0 errors.dotnet test FirstClassErrors.sln— all green (991 total, 0 failed), including the merged-inRequestBinder.PropertyTestslist-binding invariants which independently exercise the changed converters.FirstClassErrors.Analyzers.UnitTests) — 85 passed, and the dogfooded analyzers accept the parameterized factories.GenDoc's catalog still documents the default
REQUEST_ARGUMENT_*codes (the documented example lambdas produce the defaults). The branch is merged up to date withmain.Documentation
doc/updated — RequestBinder guide (EN), new "Structural error codes" section.RequestBinder.fr.md) and ADR-0016 (.fr.md), in lockstep.Architecture decisions
Proposed: ADR-0016ADR-0016 records the public-API-contract decision (the two structural codes carried on
RequestBinderOptions). Drafted asProposed; the maintainer decides its status. It was renumbered during merges withmain(which took 0014 for the required-list contract #177, and 0015 for the Combine arity ceiling #178). The documentation half — surfacing an overridden code in a consumer's generated catalog without drifting from runtime — is deferred (relates to #140).Related issues
Closes #147
🤖 Generated with Claude Code