Skip to content

Make failingendpoint address non-nullable to match usage - #5822

Merged
rbev merged 2 commits into
masterfrom
rhys/non-nullable-address
Aug 25, 2026
Merged

Make failingendpoint address non-nullable to match usage#5822
rbev merged 2 commits into
masterfrom
rhys/non-nullable-address

Conversation

@rbev

@rbev rbev commented Aug 24, 2026

Copy link
Copy Markdown
Contributor

Problem

FailingEndpointAddress (EF entity) and AddressOfFailingEndpoint were marked string?, but the ingestion path (FailedMessageFactory.ParseFailureDetails)
already guarantees a non-null value — it throws if the NServiceBus.FailedQ header is missing. The nullable annotation was propagated from the mutable FailureDetails DTO rather than
reflecting actual usage, leading to a nullable column, a dedicated NullableEndpointAddress migration, and defensive != null guards scattered across the EF query layer.

Changes

Made the types non-nullable with required:

  • FailureDetails.AddressOfFailingEndpointrequired string (was string?)
  • FailedMessageEntity.FailingEndpointAddress → required string (was string?)
  • RecordedFailedProcessingAttempt.FailingEndpointAddressrequired string (was string?)
  • StagingMessage.FailingEndpointAddressstring record parameter (was string?)

Refactored FailedMessageFactory.ParseFailureDetails to check the FailedQ header before constructing, using an object initializer to satisfy required.

Updated FailedMessage.ProcessingAttempt.FailureDetails default from = new() to = new() { AddressOfFailingEndpoint = string.Empty } so placeholder attempts satisfy the required
member.

Removed defensive null checks that existed only because of the nullable:

  • FailedMessageQueryFilters.FilterByQueueAddress — dropped FailingEndpointAddress != null && guard
  • FailedMessageRetryDataStore.GetRetryPendingMessages — replaced two-branch null match with simple comparison

Added EF migration MakeFailingEndpointAddressNonNull (SQL Server + PostgreSQL) to make the column NOT NULL with defaultValue: "" to safely handle any stray nulls. Updated model
snapshots and designer files.

Fixed 5 test files that constructed FailureDetails without setting AddressOfFailingEndpoint.

What's not affected

  • RavenDB — stores the domain document directly (no flattened column), no schema migration needed. Indexes and transformers read the now-non-nullable property without changes.
  • API/view models (FailedMessageView.QueueAddress, QueueAddress.PhysicalAddress) remain string? — non-nullable → nullable assignment is fine.

@rbev
rbev marked this pull request as ready for review August 24, 2026 08:10
@rbev
rbev merged commit 13d2166 into master Aug 25, 2026
36 checks passed
@rbev
rbev deleted the rhys/non-nullable-address branch August 25, 2026 04:16

public Dictionary<string, object> MessageMetadata { get; set; }
public FailureDetails FailureDetails { get; set; } = new();
public FailureDetails FailureDetails { get; set; } = new() { AddressOfFailingEndpoint = string.Empty };

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Should FailureDetails rather be made required and not initialise the failing endpoint to an empty string? This sorta keeps it honest then but not sure what the downstream effect is

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I'll look into it

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The blast radius of making that property required kinda sucks!

because the failed message attempts are synthesized they don't actually have failuredetails, we only actually consume the last one (so that one works correctly) but the model still contains a list.

It's a much bigger change to get rid of that and requires a bunch of Raven changes, or it requires us storing all the historical failures in EF persisters which is also quite a change.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is there a consequence of going from a null address to an empty address? Im just thinking if something is checking for a null in a query, but now its an empty string instead.

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.

3 participants