diff --git a/src/DiffEngineViewer.Tests/AttachedViewerTests.cs b/src/DiffEngineViewer.Tests/AttachedViewerTests.cs
index bb8f4314..d09181f6 100644
--- a/src/DiffEngineViewer.Tests/AttachedViewerTests.cs
+++ b/src/DiffEngineViewer.Tests/AttachedViewerTests.cs
@@ -151,6 +151,33 @@ public async Task ABusyOwnerDoesNotReadAsDead()
_ = listening;
}
+ ///
+ /// An owner that answers is an owner, whatever it answered.
+ ///
+ /// ViewerServer turns any exception in the listing handler into an error reply, and this side
+ /// read one of those as the owner having gone: the window closed with "The queue owner is no
+ /// longer running." over a single transient throw, taking the queue it was displaying with it.
+ ///
+ ///
+ [Test]
+ public async Task AnErrorReplyIsNotADeadOwner()
+ {
+ await Assert.That(ViewerServer.TryBind(0, out var bound)).IsTrue();
+ using var server = bound!;
+ using var cancel = new CancelSource();
+ var listening = server.Listen(
+ _ => ViewerResponse.Error("The listing could not be built"),
+ cancel.Token);
+ var host = new SessionHost(SessionState.Start(ViewerMode.Inline, Fixtures.Columns, Fixtures.Rows));
+
+ await Assert.That(new OwnerLink(host, server.Port).Pump()).IsTrue();
+
+ await Assert.That(host.State.Message).IsEqualTo("The listing could not be built");
+ await Assert.That(host.State.Exit).IsFalse();
+ await cancel.CancelAsync();
+ _ = listening;
+ }
+
///
/// The owner has no window of its own, so raising, hiding and closing come back on a listing
/// rather than being pushed at a port this process does not hold.
diff --git a/src/DiffEngineViewer/Ipc/OwnerLink.cs b/src/DiffEngineViewer/Ipc/OwnerLink.cs
index 1d800e2c..695466af 100644
--- a/src/DiffEngineViewer/Ipc/OwnerLink.cs
+++ b/src/DiffEngineViewer/Ipc/OwnerLink.cs
@@ -61,12 +61,24 @@ public bool Pump(out bool sent)
message = Send(command);
}
- if (!ViewerClient.TrySend(new(ViewerVerb.ListFull), out var response, port, Wait) ||
- !response.Ok)
+ if (!ViewerClient.TrySend(new(ViewerVerb.ListFull), out var response, port, Wait))
{
return false;
}
+ if (!response.Ok)
+ {
+ // An owner that answers is an owner. ViewerServer turns any exception in the listing
+ // handler into an error reply, so reading one as death closed this window over a
+ // single transient throw and lost the queue it was displaying. Said instead, and asked
+ // again on the next pass.
+ host.Mutate(_ => _ with
+ {
+ Message = response.Message ?? "The queue owner refused the listing."
+ });
+ return true;
+ }
+
var pending = InlineQueue.From(ViewerListing.Pending(response.Items));
var changes = ReadChanges(response);
host.Mutate(_ => ViewerSession.Sync(_, pending, changes, message));