Skip to content

Implement "manufacturing an error never throws" doctrine#35

Merged
Reefact merged 1 commit into
mainfrom
claude/error-api-doctrine-inconsistency-764l30
Jul 6, 2026
Merged

Implement "manufacturing an error never throws" doctrine#35
Reefact merged 1 commit into
mainfrom
claude/error-api-doctrine-inconsistency-764l30

Conversation

@Reefact

@Reefact Reefact commented Jul 6, 2026

Copy link
Copy Markdown
Owner

Summary

Refactor error construction to follow a "manufacturing an error never throws" doctrine: invalid or missing inputs now degrade to documented, visible fallbacks instead of being rejected with exceptions. This makes error creation more resilient on failure paths (inside catch blocks, during logging, etc.) where secondary exceptions would mask the original problem.

Type of change

  • New feature
  • Refactoring
  • Tests

Changes

  • Error message validation: Changed from throwing ArgumentNullException / ArgumentException to coalescing missing or whitespace mandatory messages to sentinel values:

    • null or whitespace DiagnosticMessageError.MissingDiagnosticMessage (#MISSING_DIAGNOSTIC_MESSAGE)
    • null or whitespace ShortMessageError.MissingShortMessage (#MISSING_SHORT_MESSAGE)
  • Context recording: Missing mandatory messages are now recorded in the error context under the #MISSING_REQUIRED_MESSAGE key (new ErrorContextKey.MissingRequiredMessages) as an IReadOnlyList<string> of parameter names, making the omission diagnosable.

  • Context configuration resilience: When a configureContext delegate throws, the exception is captured under the #CANNOT_BUILD_ERROR_CONTEXT key, and context entries added before the failure are preserved instead of being discarded.

  • Null inner errors: Silently dropped instead of rejected (consistent with the doctrine).

  • Test updates:

    • Converted tests that expected exceptions (ANullDiagnosticMessageIsRejected, AnEmptyOrWhitespaceDiagnosticMessageIsRejected, ANullShortMessageIsRejected, AnEmptyOrWhitespaceShortMessageIsRejected) to verify fallback behavior and context recording.
    • Added new tests to verify missing message recording in context and preservation of context entries when configuration fails.
    • Added [Collection("SmartEnumSideEffects")] and IDisposable to ErrorDefensiveTests for proper test isolation.
  • API simplification: Removed early validation in DomainError.Create(), InfrastructureError.Create(), PrimaryPortError.Create(), and SecondaryPortError.Create() — all normalization now happens in the base Error constructor.

  • Documentation: Updated XML docs in Error, PublicMessageStage, PrimaryPortInnerErrors, SecondaryPortInnerErrors, and ErrorContextKey to reflect the new doctrine and behavior.

Testing

  • dotnet build FirstClassErrors.sln
  • dotnet test FirstClassErrors.sln

All existing tests pass; new tests verify the fallback behavior and context recording.

Documentation

  • Public API / error documentation updated

Updated XML documentation in Error.cs, PublicMessageStage.cs, ErrorContextKey.cs, and related classes to document the "manufacturing an error never throws" doctrine and the new sentinel values and context keys.

https://claude.ai/code/session_01URc4t7KDuwgbq1yU4E6y6t

Manufacturing an Error now never throws. Previously the same constructor mixed
three policies for invalid input: silently default the code, throw on missing
mandatory messages, and swallow a failing context configuration — the worst of
both worlds (no fail-fast guarantee, yet silent defaults still hide real
mistakes).

- Missing (null/whitespace) diagnostic and short messages now fall back to a
  visible, documented sentinel (#MISSING_DIAGNOSTIC_MESSAGE /
  #MISSING_SHORT_MESSAGE) and the omission is recorded in the context under the
  #MISSING_REQUIRED_MESSAGE key, instead of throwing.
- A configureContext delegate that throws now preserves the entries it added
  before failing, alongside #CANNOT_BUILD_ERROR_CONTEXT, rather than discarding
  the partial context.
- Message normalization is centralized in the base Error constructor, removing
  the duplicated validation previously done in the Create factories and in
  WithPublicMessage.
- Aligned the PrimaryPort/SecondaryPort inner-error Add docs with the actual
  behavior: a null error is ignored, not rejected (the docs previously said
  "Must not be null").

Null enumerable inner-error entries remain filtered out (unchanged).

Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01URc4t7KDuwgbq1yU4E6y6t
@Reefact Reefact force-pushed the claude/error-api-doctrine-inconsistency-764l30 branch from 78acb35 to 2a53b53 Compare July 6, 2026 21:06
@Reefact Reefact merged commit 6e196b3 into main Jul 6, 2026
2 checks passed
@Reefact Reefact deleted the claude/error-api-doctrine-inconsistency-764l30 branch July 6, 2026 21:11
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants