Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
15 changes: 10 additions & 5 deletions src/MeshWeaver.Messaging.Hub/MessageHub.cs
Original file line number Diff line number Diff line change
Expand Up @@ -792,6 +792,7 @@ private IMessageDelivery FinishDelivery(IMessageDelivery delivery)
logger.LogWarning(
"Unhandled {MessageType} (ID: {MessageId}) in fallback hub {Address} - answering {ErrorType} NACK: {Reason}",
delivery.Message.GetType().Name, delivery.Id, Address, nackPolicy.ErrorType, nackPolicy.Reason);
var nackPosted = false;
try
{
Post(new DeliveryFailure(delivery)
Expand All @@ -800,17 +801,21 @@ private IMessageDelivery FinishDelivery(IMessageDelivery delivery)
NodeTypePath = nackPolicy.NodeTypePath,
Message = nackPolicy.Reason
}, o => o.ResponseFor(delivery));
nackPosted = true;
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to post fallback NACK for {MessageType} (ID: {MessageId}) in {Address}",
delivery.Message.GetType().Name, delivery.Id, Address);
}
// The typed NACK above IS this request's failure response — mark it so no
// unclassified follow-up is posted for the same delivery (see
// MessageService.FailureAlreadyReported).
return delivery.Failed(nackPolicy.Reason)
.WithProperty(MessageService.FailureAlreadyReported, true);
// 🚨 Mark ONLY when the typed NACK actually went out — see
// MessageService.FailureAlreadyReported. Marking after a THROWN post would suppress
// the follow-up and leave the caller with NO failure response, which is the silent
// park this NACK path exists to prevent.
var failed = delivery.Failed(nackPolicy.Reason);
return nackPosted
? failed.WithProperty(MessageService.FailureAlreadyReported, true)
: failed;
}

// Check if this is a request that expects a response
Expand Down
13 changes: 9 additions & 4 deletions src/MeshWeaver.Messaging.Hub/MessageService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1413,6 +1413,7 @@ private IMessageDelivery DeserializeDelivery(IMessageDelivery delivery)
var reason = $"{nackPolicy.Reason} (inbound type '{jsonType}' is not registered in this hub's TypeRegistry)";
logger.LogWarning("Unhandled {JsonType} in fallback hub {Address} - answering {ErrorType} NACK: {Reason}",
jsonType, Address, nackPolicy.ErrorType, reason);
var nackPosted = false;
try
{
Post(new DeliveryFailure(delivery)
Expand All @@ -1421,16 +1422,20 @@ private IMessageDelivery DeserializeDelivery(IMessageDelivery delivery)
NodeTypePath = nackPolicy.NodeTypePath,
Message = reason
}, new PostOptions(Address).ResponseFor(delivery));
nackPosted = true;
}
catch (Exception ex)
{
logger.LogError(ex, "Failed to post fallback NACK for '{JsonType}' (ID: {MessageId}) in {Address}",
jsonType, delivery.Id, Address);
}
// The typed NACK above IS this request's failure response. Mark it so the
// Failed-state check on the way out does not post an unclassified second one and
// race it to the caller's subject.
return delivery.Failed(reason).WithProperty(FailureAlreadyReported, true);
// 🚨 Mark ONLY when the typed NACK actually went out. The marker suppresses the
// unclassified follow-up, so setting it after a THROWN post would leave the caller
// with no failure response at all — turning a swallowed exception into the silent
// park this whole NACK path exists to prevent. Suppress the duplicate, never the
// only answer.
var failed = delivery.Failed(reason);
return nackPosted ? failed.WithProperty(FailureAlreadyReported, true) : failed;
}

return ReportFailure(delivery.Failed(failureMessage));
Expand Down
Loading