From 67cbf96ce620cd52435e55b52ca32116f5e148e8 Mon Sep 17 00:00:00 2001 From: Nikola Jokic Date: Thu, 16 Jul 2026 17:10:25 +0200 Subject: [PATCH] Cleanup session files on get message or session deleted error (#4551) --- src/Runner.Listener/Runner.cs | 11 +++- src/Test/L0/Listener/RunnerL0.cs | 92 ++++++++++++++++++++++++++++++++ 2 files changed, 102 insertions(+), 1 deletion(-) diff --git a/src/Runner.Listener/Runner.cs b/src/Runner.Listener/Runner.cs index 4c27c3619..31c871302 100644 --- a/src/Runner.Listener/Runner.cs +++ b/src/Runner.Listener/Runner.cs @@ -498,6 +498,7 @@ namespace GitHub.Runner.Listener bool skipSessionDeletion = false; bool restartSession = false; // Flag to indicate session restart bool restartSessionPending = false; + bool cleanupLocalConfigAfter404 = false; try { var notification = HostContext.GetService(); @@ -819,6 +820,14 @@ namespace GitHub.Runner.Listener Trace.Error($"Received message {message.MessageId} with unsupported message type {message.MessageType}."); } } + catch (Exception ex) when (ex is TaskAgentNotFoundException || ex is RunnerNotFoundException) + { + Trace.Info($"Runner registration no longer exists while retrieving messages. {ex.Message}"); + _term.WriteError("The runner no longer exists on the server. Cleaning up local configuration."); + skipSessionDeletion = true; + cleanupLocalConfigAfter404 = true; + break; + } finally { if (!skipMessageDeletion && message != null) @@ -865,7 +874,7 @@ namespace GitHub.Runner.Listener messageQueueLoopTokenSource.Dispose(); - if (settings.Ephemeral && runOnceJobCompleted) + if ((settings.Ephemeral && runOnceJobCompleted) || cleanupLocalConfigAfter404) { configManager.DeleteLocalRunnerConfig(); } diff --git a/src/Test/L0/Listener/RunnerL0.cs b/src/Test/L0/Listener/RunnerL0.cs index ba7f42e88..fcf442244 100644 --- a/src/Test/L0/Listener/RunnerL0.cs +++ b/src/Test/L0/Listener/RunnerL0.cs @@ -29,6 +29,7 @@ namespace GitHub.Runner.Common.Tests.Listener private Mock _credentialManager; private Mock _actionsRunServer; private Mock _runServer; + private Mock _brokerServer; private readonly string _returnJobResultForHosted; public RunnerL0() @@ -46,6 +47,7 @@ namespace GitHub.Runner.Common.Tests.Listener _credentialManager = new Mock(); _actionsRunServer = new Mock(); _runServer = new Mock(); + _brokerServer = new Mock(); _returnJobResultForHosted = Environment.GetEnvironmentVariable("ACTIONS_RUNNER_RETURN_JOB_RESULT_FOR_HOSTED"); Environment.SetEnvironmentVariable("ACTIONS_RUNNER_RETURN_JOB_RESULT_FOR_HOSTED", null); @@ -175,6 +177,96 @@ namespace GitHub.Runner.Common.Tests.Listener } } + [Fact] + [Trait("Level", "L0")] + [Trait("Category", "Runner")] + public async Task TestRunAsyncCleanupLocalConfigWhenGetNextMessageReturnsNotFound() + { + using (var hc = new TestHostContext(this)) + { + //Arrange + var runner = new Runner.Listener.Runner(); + hc.SetSingleton(_configurationManager.Object); + hc.SetSingleton(_jobNotification.Object); + hc.SetSingleton(_promptManager.Object); + hc.SetSingleton(_runnerServer.Object); + hc.SetSingleton(_brokerServer.Object); + hc.SetSingleton(_credentialManager.Object); + hc.SetSingleton(_configStore.Object); + hc.EnqueueInstance(_acquireJobThrottler.Object); + hc.EnqueueInstance(_jobDispatcher.Object); + + var messageListener = new MessageListener(); + messageListener.Initialize(hc); + hc.SetSingleton(messageListener); + + runner.Initialize(hc); + + var settings = new RunnerSettings + { + AgentId = 1, + AgentName = "myagent", + PoolId = 43242, + PoolName = "default", + ServerUrl = "http://myserver", + WorkFolder = "_work", + Ephemeral = false, + }; + + _configurationManager.Setup(x => x.LoadSettings()) + .Returns(settings); + _configurationManager.Setup(x => x.IsConfigured()) + .Returns(true); + _credentialManager.Setup(x => x.LoadCredentials(false)).Returns(new VssCredentials()); + _runnerServer.Setup(x => x.ConnectAsync(It.IsAny(), It.IsAny())) + .Returns(Task.CompletedTask); + _runnerServer.Setup(x => x.CreateAgentSessionAsync( + settings.PoolId, + It.Is(x => x != null), + It.IsAny())) + .Returns(Task.FromResult(new TaskAgentSession())); + _runnerServer.Setup(x => x.GetAgentMessageAsync( + settings.PoolId, + It.IsAny(), + It.IsAny(), + TaskAgentStatus.Online, + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny())) + .Throws(new TaskAgentNotFoundException("runner not found")); + _jobNotification.Setup(x => x.StartClient(It.IsAny())); + _configStore.Setup(x => x.IsServiceConfigured()).Returns(false); + + //Act + var command = new CommandSettings(hc, new string[] { "run" }); + var result = await runner.ExecuteCommand(command); + + //Assert + Assert.Equal(Constants.Runner.ReturnCode.Success, result); + _runnerServer.Verify(x => x.CreateAgentSessionAsync( + settings.PoolId, + It.Is(x => x != null), + It.IsAny()), Times.Once()); + _runnerServer.Verify(x => x.GetAgentMessageAsync( + settings.PoolId, + It.IsAny(), + It.IsAny(), + TaskAgentStatus.Online, + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny(), + It.IsAny()), Times.Once()); + _runnerServer.Verify(x => x.DeleteAgentSessionAsync( + It.IsAny(), + It.IsAny(), + It.IsAny()), Times.Never()); + _configurationManager.Verify(x => x.DeleteLocalRunnerConfig(), Times.Once()); + } + } + public static TheoryData RunAsServiceTestData = new TheoryData() { // staring with run command, configured as run as service, should start the runner