<Project>
     <PropertyGroup>
-        <Version>3.3</Version>
+        <Version>3.4</Version>
     </PropertyGroup>
 </Project>
 
     AutoOnEnterArea,
     OverworldEnemies,
     FateEnemies,
+    QuestInterruption,
 }
 
                 Module = combatModule,
                 Data = combatData,
             };
+            _wasInCombat = combatData.SpawnType == EEnemySpawnType.QuestInterruption;
             return true;
         }
         else
         if (_currentFight == null)
             return EStatus.Complete;
 
-        if (_movementController.IsPathfinding || _movementController.IsPathRunning || _movementController.MovementStartedAt > DateTime.Now.AddSeconds(-1))
+        if (_movementController.IsPathfinding ||
+            _movementController.IsPathRunning ||
+            _movementController.MovementStartedAt > DateTime.Now.AddSeconds(-1))
             return EStatus.Moving;
 
         var target = _targetManager.Target;
         else
         {
             var nextTarget = FindNextTarget();
+            _logger.LogInformation("NT → {NT}", nextTarget);
+
             if (nextTarget is { IsDead: false })
                 SetTarget(nextTarget);
         }
 
     public sealed class CombatData
     {
-        public required ElementId ElementId { get; init; }
+        public required ElementId? ElementId { get; init; }
         public required EEnemySpawnType SpawnType { get; init; }
         public required List<uint> KillEnemyDataIds { get; init; }
         public required List<ComplexCombatData> ComplexCombatDatas { get; init; }
 
     public enum EStatus
     {
+        NotStarted,
         InCombat,
         Moving,
         Complete,
 
                 PrintMountId();
                 break;
 
+            case "handle-interrupt":
+                _questController.InterruptQueueWithCombat();
+                break;
+
             case "":
                 _questWindow.Toggle();
                 break;
 
             return EStatus.Complete;
         }
 
-        if (_currentTask == null && _taskQueue.Count == 0)
+        if (_taskQueue.AllTasksComplete)
             GoToNextNode();
 
         UpdateCurrentTask();
     public override void Stop(string label)
     {
         _currentRequest = null;
-        _currentTask = null;
-        _taskQueue.Clear();
+        _taskQueue.Reset();
     }
 
     private void GoToNextNode()
         if (_currentRequest == null)
             return;
 
-        if (_taskQueue.Count > 0)
+        if (!_taskQueue.AllTasksComplete)
             return;
 
         var director = UIState.Instance()->DirectorTodo.Director;
 
     public override IList<string> GetRemainingTaskNames()
     {
-        if (_currentTask != null)
-            return [_currentTask.ToString() ?? "?", .. base.GetRemainingTaskNames()];
+        if (_taskQueue.CurrentTask is {} currentTask)
+            return [currentTask.ToString() ?? "?", .. base.GetRemainingTaskNames()];
         else
             return base.GetRemainingTaskNames();
     }
     {
         if (_revisitRegex.IsMatch(message.TextValue))
         {
-            if (_currentTask is IRevisitAware currentTaskRevisitAware)
+            if (_taskQueue.CurrentTask is IRevisitAware currentTaskRevisitAware)
                 currentTaskRevisitAware.OnRevisit();
 
-            foreach (ITask task in _taskQueue)
+            foreach (ITask task in _taskQueue.RemainingTasks)
             {
                 if (task is IRevisitAware taskRevisitAware)
                     taskRevisitAware.OnRevisit();
 
 {
     protected readonly IChatGui _chatGui;
     protected readonly ILogger<T> _logger;
+    protected readonly TaskQueue _taskQueue = new();
 
-    protected readonly Queue<ITask> _taskQueue = new();
-    protected ITask? _currentTask;
-
-    public MiniTaskController(IChatGui chatGui, ILogger<T> logger)
+    protected MiniTaskController(IChatGui chatGui, ILogger<T> logger)
     {
         _chatGui = chatGui;
         _logger = logger;
 
     protected virtual void UpdateCurrentTask()
     {
-        if (_currentTask == null)
+        if (_taskQueue.CurrentTask == null)
         {
             if (_taskQueue.TryDequeue(out ITask? upcomingTask))
             {
                     _logger.LogInformation("Starting task {TaskName}", upcomingTask.ToString());
                     if (upcomingTask.Start())
                     {
-                        _currentTask = upcomingTask;
+                        _taskQueue.CurrentTask = upcomingTask;
                         return;
                     }
                     else
         ETaskResult result;
         try
         {
-            result = _currentTask.Update();
+            result = _taskQueue.CurrentTask.Update();
         }
         catch (Exception e)
         {
-            _logger.LogError(e, "Failed to update task {TaskName}", _currentTask.ToString());
+            _logger.LogError(e, "Failed to update task {TaskName}", _taskQueue.CurrentTask.ToString());
             _chatGui.PrintError(
-                $"[Questionable] Failed to update task '{_currentTask}', please check /xllog for details.");
+                $"[Questionable] Failed to update task '{_taskQueue.CurrentTask}', please check /xllog for details.");
             Stop("Task failed to update");
             return;
         }
 
             case ETaskResult.SkipRemainingTasksForStep:
                 _logger.LogInformation("{Task} → {Result}, skipping remaining tasks for step",
-                    _currentTask, result);
-                _currentTask = null;
+                    _taskQueue.CurrentTask, result);
+                _taskQueue.CurrentTask = null;
 
                 while (_taskQueue.TryDequeue(out ITask? nextTask))
                 {
                     if (nextTask is ILastTask or Gather.SkipMarker)
                     {
-                        _currentTask = nextTask;
+                        _taskQueue.CurrentTask = nextTask;
                         return;
                     }
                 }
 
             case ETaskResult.TaskComplete:
                 _logger.LogInformation("{Task} → {Result}, remaining tasks: {RemainingTaskCount}",
-                    _currentTask, result, _taskQueue.Count);
+                    _taskQueue.CurrentTask, result, _taskQueue.RemainingTasks.Count());
 
-                OnTaskComplete(_currentTask);
+                OnTaskComplete(_taskQueue.CurrentTask);
 
-                _currentTask = null;
+                _taskQueue.CurrentTask = null;
 
                 // handled in next update
                 return;
 
             case ETaskResult.NextStep:
-                _logger.LogInformation("{Task} → {Result}", _currentTask, result);
+                _logger.LogInformation("{Task} → {Result}", _taskQueue.CurrentTask, result);
 
-                var lastTask = (ILastTask)_currentTask;
-                _currentTask = null;
+                var lastTask = (ILastTask)_taskQueue.CurrentTask;
+                _taskQueue.CurrentTask = null;
 
                 OnNextStep(lastTask);
                 return;
 
             case ETaskResult.End:
-                _logger.LogInformation("{Task} → {Result}", _currentTask, result);
-                _currentTask = null;
+                _logger.LogInformation("{Task} → {Result}", _taskQueue.CurrentTask, result);
+                _taskQueue.CurrentTask = null;
                 Stop("Task end");
                 return;
         }
     public abstract void Stop(string label);
 
     public virtual IList<string> GetRemainingTaskNames() =>
-        _taskQueue.Select(x => x.ToString() ?? "?").ToList();
+        _taskQueue.RemainingTasks.Select(x => x.ToString() ?? "?").ToList();
 }
 
 
     public bool IsPathfinding => _pathfindTask is { IsCompleted: false };
     public DestinationData? Destination { get; set; }
-    public DateTime MovementStartedAt { get; private set; } = DateTime.MaxValue;
+    public DateTime MovementStartedAt { get; private set; } = DateTime.Now;
 
     public void Update()
     {
 
 using Dalamud.Game.Text.SeStringHandling;
 using Dalamud.Plugin.Services;
 using FFXIVClientStructs.FFXIV.Client.Game;
+using LLib;
 using LLib.GameData;
+using Lumina.Excel.GeneratedSheets;
 using Microsoft.Extensions.Logging;
 using Questionable.Controller.Steps;
 using Questionable.Controller.Steps.Interactions;
 using Questionable.Functions;
 using Questionable.Model;
 using Questionable.Model.Questing;
+using Quest = Questionable.Model.Quest;
+using Mount = Questionable.Controller.Steps.Common.Mount;
 
 namespace Questionable.Controller;
 
     private readonly Configuration _configuration;
     private readonly YesAlreadyIpc _yesAlreadyIpc;
     private readonly TaskCreator _taskCreator;
+    private readonly Mount.Factory _mountFactory;
+    private readonly Combat.Factory _combatFactory;
+
+    private readonly string _actionCanceledText;
 
     private readonly object _progressLock = new();
 
         IToastGui toastGui,
         Configuration configuration,
         YesAlreadyIpc yesAlreadyIpc,
-        TaskCreator taskCreator)
+        TaskCreator taskCreator,
+        Mount.Factory mountFactory,
+        Combat.Factory combatFactory,
+        IDataManager dataManager)
         : base(chatGui, logger)
     {
         _clientState = clientState;
         _configuration = configuration;
         _yesAlreadyIpc = yesAlreadyIpc;
         _taskCreator = taskCreator;
+        _mountFactory = mountFactory;
+        _combatFactory = combatFactory;
 
         _condition.ConditionChange += OnConditionChange;
         _toastGui.Toast += OnNormalToast;
         _toastGui.ErrorToast += OnErrorToast;
+
+        _actionCanceledText = dataManager.GetString<LogMessage>(1314, x => x.Text)!;
     }
 
     public EAutomationType AutomationType
 
         if (!_clientState.IsLoggedIn || _condition[ConditionFlag.Unconscious])
         {
-            if (_currentTask != null || _taskQueue.Count > 0)
+            if (!_taskQueue.AllTasksComplete)
             {
                 Stop("HP = 0");
                 _movementController.Stop();
         }
         else if (_configuration.General.UseEscToCancelQuesting && _keyState[VirtualKey.ESCAPE])
         {
-            if (_currentTask != null || _taskQueue.Count > 0)
+            if (!_taskQueue.AllTasksComplete)
             {
                 Stop("ESC pressed");
                 _movementController.Stop();
             return;
 
         if (AutomationType == EAutomationType.Automatic &&
-            ((_currentTask == null && _taskQueue.Count == 0) ||
-             _currentTask is WaitAtEnd.WaitQuestAccepted)
+            (_taskQueue.AllTasksComplete || _taskQueue.CurrentTask is WaitAtEnd.WaitQuestAccepted)
             && CurrentQuest is { Sequence: 0, Step: 0 } or { Sequence: 0, Step: 255 }
             && DateTime.Now >= CurrentQuest.StepProgress.StartedAt.AddSeconds(15))
         {
                 questToRun = _nextQuest;
                 currentSequence = _nextQuest.Sequence; // by definition, this should always be 0
                 if (_nextQuest.Step == 0 &&
-                    _currentTask == null &&
-                    _taskQueue.Count == 0 &&
+                    _taskQueue.AllTasksComplete &&
                     AutomationType == EAutomationType.Automatic)
                     ExecuteNextStep();
             }
                 questToRun = _gatheringQuest;
                 currentSequence = _gatheringQuest.Sequence;
                 if (_gatheringQuest.Step == 0 &&
-                    _currentTask == null &&
-                    _taskQueue.Count == 0 &&
+                    _taskQueue.AllTasksComplete &&
                     AutomationType == EAutomationType.Automatic)
                     ExecuteNextStep();
             }
             if (questToRun.Step == 255)
             {
                 DebugState = "Step completed";
-                if (_currentTask != null || _taskQueue.Count > 0)
+                if (!_taskQueue.AllTasksComplete)
                     CheckNextTasks("Step complete");
                 return;
             }
     private void ClearTasksInternal()
     {
         //_logger.LogDebug("Clearing task (internally)");
-        _currentTask = null;
-
-        if (_taskQueue.Count > 0)
-            _taskQueue.Clear();
+        _taskQueue.Reset();
 
         _yesAlreadyIpc.RestoreYesAlready();
         _combatController.Stop("ClearTasksInternal");
 
     public string ToStatString()
     {
-        return _currentTask == null ? $"- (+{_taskQueue.Count})" : $"{_currentTask} (+{_taskQueue.Count})";
+        return _taskQueue.CurrentTask is { } currentTask
+            ? $"{currentTask} (+{_taskQueue.RemainingTasks.Count()})"
+            : $"- (+{_taskQueue.RemainingTasks.Count()})";
     }
 
     public bool HasCurrentTaskMatching<T>([NotNullWhen(true)] out T? task)
         where T : class, ITask
     {
-        if (_currentTask is T t)
+        if (_taskQueue.CurrentTask is T t)
         {
             task = t;
             return true;
         }
     }
 
-    public bool IsRunning => _currentTask != null || _taskQueue.Count > 0;
+    public bool IsRunning => !_taskQueue.AllTasksComplete;
 
     public sealed class QuestProgress
     {
     {
         lock (_progressLock)
         {
-            if (_currentTask is ISkippableTask)
-                _currentTask = null;
-            else if (_currentTask != null)
+            if (_taskQueue.CurrentTask is ISkippableTask)
+                _taskQueue.CurrentTask = null;
+            else if (_taskQueue.CurrentTask != null)
             {
-                _currentTask = null;
-                while (_taskQueue.Count > 0)
+                _taskQueue.CurrentTask = null;
+                while (_taskQueue.TryPeek(out ITask? task))
                 {
-                    var task = _taskQueue.Dequeue();
+                    _taskQueue.TryDequeue(out _);
                     if (task is ISkippableTask)
                         return;
                 }
 
-                if (_taskQueue.Count == 0)
+                if (_taskQueue.AllTasksComplete)
                 {
                     Stop("Skip");
                     IncreaseStepCount(elementId, currentQuestSequence);
 
     public void SkipSimulatedTask()
     {
-        _currentTask = null;
+        _taskQueue.CurrentTask = null;
     }
 
     public bool IsInterruptible()
 
     private void OnConditionChange(ConditionFlag flag, bool value)
     {
-        if (_currentTask is IConditionChangeAware conditionChangeAware)
+        if (_taskQueue.CurrentTask is IConditionChangeAware conditionChangeAware)
             conditionChangeAware.OnConditionChange(flag, value);
     }
 
 
     private void OnErrorToast(ref SeString message, ref bool isHandled)
     {
-        if (_currentTask is IToastAware toastAware)
+        _logger.LogWarning("XXX {A} → {B} XXX", _actionCanceledText, message.TextValue);
+        if (_taskQueue.CurrentTask is IToastAware toastAware)
         {
             if (toastAware.OnErrorToast(message))
             {
                 isHandled = true;
             }
         }
+
+        if (!isHandled)
+        {
+            if (GameFunctions.GameStringEquals(_actionCanceledText, message.TextValue) &&
+                !_condition[ConditionFlag.InFlight])
+                InterruptQueueWithCombat();
+        }
+    }
+
+    public void InterruptQueueWithCombat()
+    {
+        _logger.LogWarning("Interrupted with action canceled message, attempting to resolve");
+        List<ITask> tasks = [];
+        if (_condition[ConditionFlag.Mounted])
+            tasks.Add(_mountFactory.Unmount());
+
+        tasks.Add(_combatFactory.CreateTask(null, false, EEnemySpawnType.QuestInterruption, [], [], []));
+        tasks.Add(new WaitAtEnd.WaitDelay());
+        _taskQueue.InterruptWith(tasks);
     }
 
     public void Dispose()
 
         private bool _mountTriggered;
         private DateTime _retryAt = DateTime.MinValue;
 
+        public bool ShouldRedoOnInterrupt() => true;
+
         public bool Start()
         {
             if (condition[ConditionFlag.Mounted])
         private bool _unmountTriggered;
         private DateTime _continueAt = DateTime.MinValue;
 
+        public bool ShouldRedoOnInterrupt() => true;
+
         public bool Start()
         {
             if (!condition[ConditionFlag.Mounted])
 
 
 internal interface ITask
 {
+    bool ShouldRedoOnInterrupt() => false;
+
     bool Start();
 
     ETaskResult Update();
 
                 step.CompletionQuestVariablesFlags, step.ComplexCombatData);
         }
 
-        private HandleCombat CreateTask(ElementId elementId, bool isLastStep, EEnemySpawnType enemySpawnType,
+        internal HandleCombat CreateTask(ElementId? elementId, bool isLastStep, EEnemySpawnType enemySpawnType,
             IList<uint> killEnemyDataIds, IList<QuestWorkValue?> completionQuestVariablesFlags,
             IList<ComplexCombatData> complexCombatData)
         {
         }
     }
 
-    private sealed class HandleCombat(
+    internal sealed class HandleCombat(
         bool isLastStep,
         CombatController.CombatData combatData,
         IList<QuestWorkValue?> completionQuestVariableFlags,
         CombatController combatController,
         QuestFunctions questFunctions) : ITask
     {
+        private CombatController.EStatus _status = CombatController.EStatus.NotStarted;
+
         public bool Start() => combatController.Start(combatData);
 
         public ETaskResult Update()
         {
-            if (combatController.Update() != CombatController.EStatus.Complete)
+            _status = combatController.Update();
+            if (_status != CombatController.EStatus.Complete)
                 return ETaskResult.StillRunning;
 
             // if our quest step has any completion flags, we need to check if they are set
         public override string ToString()
         {
             if (QuestWorkUtils.HasCompletionFlags(completionQuestVariableFlags))
-                return "HandleCombat(wait: QW flags)";
+                return $"HandleCombat(wait: QW flags, s: {_status})";
             else if (isLastStep)
-                return "HandleCombat(wait: next sequence)";
+                return $"HandleCombat(wait: next sequence, s: {_status})";
             else
-                return "HandleCombat(wait: not in combat)";
+                return $"HandleCombat(wait: not in combat, s: {_status})";
         }
     }
 }
 
             _canRestart = moveParams.RestartNavigation;
         }
 
+        public bool ShouldRedoOnInterrupt() => true;
+
         public bool Start()
         {
             float stopDistance = _moveParams.StopDistance ?? QuestStep.DefaultStopDistance;
         GameFunctions gameFunctions,
         IClientState clientState) : ITask
     {
+        public bool ShouldRedoOnInterrupt() => true;
+
         public bool Start() => true;
 
         public ETaskResult Update()
         private bool _landing;
         private DateTime _continueAt;
 
+        public bool ShouldRedoOnInterrupt() => true;
+
         public bool Start()
         {
             if (!condition[ConditionFlag.InFlight])
 
--- /dev/null
+using System.Collections.Generic;
+using System.Diagnostics.CodeAnalysis;
+using System.Linq;
+
+namespace Questionable.Controller.Steps;
+
+internal sealed class TaskQueue
+{
+    private readonly List<ITask> _tasks = [];
+    private int _currentTaskIndex;
+    public ITask? CurrentTask { get; set; }
+
+    public IEnumerable<ITask> RemainingTasks => _tasks.Skip(_currentTaskIndex);
+    public bool AllTasksComplete => CurrentTask == null && _currentTaskIndex >= _tasks.Count;
+
+    public void Enqueue(ITask task)
+    {
+        _tasks.Add(task);
+    }
+
+    public bool TryDequeue([NotNullWhen(true)] out ITask? task)
+    {
+        if (_currentTaskIndex >= _tasks.Count)
+        {
+            task = null;
+            return false;
+        }
+
+        task = _tasks[_currentTaskIndex];
+        if (task.ShouldRedoOnInterrupt())
+            _currentTaskIndex++;
+        else
+            _tasks.RemoveAt(0);
+        return true;
+    }
+
+    public bool TryPeek([NotNullWhen(true)] out ITask? task)
+    {
+        if (_currentTaskIndex >= _tasks.Count)
+        {
+            task = null;
+            return false;
+        }
+
+        task = _tasks[_currentTaskIndex];
+        return true;
+    }
+
+    public void Reset()
+    {
+        _tasks.Clear();
+        _currentTaskIndex = 0;
+        CurrentTask = null;
+    }
+
+    public void InterruptWith(List<ITask> interruptionTasks)
+    {
+        if (CurrentTask != null)
+        {
+            _tasks.Insert(0, CurrentTask);
+            CurrentTask = null;
+            _currentTaskIndex = 0;
+        }
+
+        _tasks.InsertRange(0, interruptionTasks);
+    }
+}