diff --git a/Questionable/Controller/GatheringController.cs b/Questionable/Controller/GatheringController.cs
index bbe6d2e49..28de31f9e 100644
--- a/Questionable/Controller/GatheringController.cs
+++ b/Questionable/Controller/GatheringController.cs
@@ -49,9 +49,10 @@ internal sealed unsafe class GatheringController : MiniTaskController<GatheringC
         ILogger<GatheringController> logger,
         ICondition condition,
         IServiceProvider serviceProvider,
+        InterruptHandler interruptHandler,
         IDataManager dataManager,
         IPluginLog pluginLog)
-        : base(chatGui, condition, serviceProvider, dataManager, logger)
+        : base(chatGui, condition, serviceProvider, interruptHandler, dataManager, logger)
     {
         _movementController = movementController;
         _gatheringPointRegistry = gatheringPointRegistry;
diff --git a/Questionable/Controller/InterruptHandler.cs b/Questionable/Controller/InterruptHandler.cs
new file mode 100644
index 000000000..9171432fd
--- /dev/null
+++ b/Questionable/Controller/InterruptHandler.cs
@@ -0,0 +1,165 @@
+using System;
+using System.Diagnostics.CodeAnalysis;
+using System.Runtime.InteropServices;
+using Dalamud.Game;
+using Dalamud.Hooking;
+using Dalamud.Plugin.Services;
+using FFXIVClientStructs.FFXIV.Client.Game;
+using FFXIVClientStructs.FFXIV.Client.Game.Character;
+using FFXIVClientStructs.FFXIV.Common.Math;
+using JetBrains.Annotations;
+using Microsoft.Extensions.Logging;
+using Questionable.Data;
+
+namespace Questionable.Controller;
+
+internal sealed unsafe class InterruptHandler : IDisposable
+{
+    private readonly Hook<ProcessActionEffect> _processActionEffectHook;
+    private readonly IClientState _clientState;
+    private readonly TerritoryData _territoryData;
+    private readonly ILogger<InterruptHandler> _logger;
+
+    private delegate void ProcessActionEffect(uint sourceId, Character* sourceCharacter, Vector3* pos,
+        EffectHeader* effectHeader, EffectEntry* effectArray, ulong* effectTail);
+
+    public InterruptHandler(IGameInteropProvider gameInteropProvider, IClientState clientState,
+        TerritoryData territoryData, ILogger<InterruptHandler> logger)
+    {
+        _clientState = clientState;
+        _territoryData = territoryData;
+        _logger = logger;
+        _processActionEffectHook =
+            gameInteropProvider.HookFromSignature<ProcessActionEffect>(Signatures.ActionEffect,
+                HandleProcessActionEffect);
+        _processActionEffectHook.Enable();
+    }
+
+    public event EventHandler? Interrupted;
+
+    private void HandleProcessActionEffect(uint sourceId, Character* sourceCharacter, Vector3* pos,
+        EffectHeader* effectHeader, EffectEntry* effectArray, ulong* effectTail)
+    {
+        try
+        {
+            if (!_territoryData.IsDutyInstance(_clientState.TerritoryType))
+            {
+                for (int i = 0; i < effectHeader->TargetCount; i++)
+                {
+                    uint targetId = (uint)(effectTail[i] & uint.MaxValue);
+                    EffectEntry* effect = effectArray + 8 * i;
+
+                    if (targetId == _clientState.LocalPlayer?.GameObjectId &&
+                        effect->Type is EActionEffectType.Damage or EActionEffectType.BlockedDamage
+                            or EActionEffectType.ParriedDamage)
+                    {
+                        _logger.LogTrace("Damage action effect on self, from {SourceId} ({EffectType})", sourceId,
+                            effect->Type);
+                        Interrupted?.Invoke(this, EventArgs.Empty);
+                        break;
+                    }
+                }
+            }
+        }
+        catch (Exception e)
+        {
+            _logger.LogWarning(e, "Unable to process action effect");
+        }
+        finally
+        {
+            _processActionEffectHook.Original(sourceId, sourceCharacter, pos, effectHeader, effectArray, effectTail);
+        }
+    }
+
+    public void Dispose()
+    {
+        _processActionEffectHook.Disable();
+        _processActionEffectHook.Dispose();
+    }
+
+    private static class Signatures
+    {
+        internal const string ActionEffect = "40 ?? 56 57 41 ?? 41 ?? 41 ?? 48 ?? ?? ?? ?? ?? ?? ?? 48";
+    }
+
+    [StructLayout(LayoutKind.Explicit)]
+    private struct EffectEntry
+    {
+        [FieldOffset(0)] public EActionEffectType Type;
+        [FieldOffset(1)] public byte Param0;
+        [FieldOffset(2)] public byte Param1;
+        [FieldOffset(3)] public byte Param2;
+        [FieldOffset(4)] public byte Mult;
+        [FieldOffset(5)] public byte Flags;
+        [FieldOffset(6)] public ushort Value;
+
+        public byte AttackType => (byte)(Param1 & 0xF);
+        public uint Damage => Mult == 0 ? Value : Value + ((uint)ushort.MaxValue + 1) * Mult;
+
+        public override string ToString()
+        {
+            return
+                $"Type: {Type}, p0: {Param0:D3}, p1: {Param1:D3}, p2: {Param2:D3} 0x{Param2:X2} '{Convert.ToString(Param2, 2).PadLeft(8, '0')}', mult: {Mult:D3}, flags: {Flags:D3} | {Convert.ToString(Flags, 2).PadLeft(8, '0')}, value: {Value:D6} ATTACK TYPE: {AttackType}";
+        }
+    }
+
+    [StructLayout(LayoutKind.Explicit)]
+    private struct EffectHeader
+    {
+        [FieldOffset(0)] public ulong AnimationTargetId;
+        [FieldOffset(8)] public uint ActionID;
+        [FieldOffset(12)] public uint GlobalEffectCounter;
+        [FieldOffset(16)] public float AnimationLockTime;
+        [FieldOffset(20)] public uint SomeTargetID;
+        [FieldOffset(24)] public ushort SourceSequence;
+        [FieldOffset(26)] public ushort Rotation;
+        [FieldOffset(28)] public ushort AnimationId;
+        [FieldOffset(30)] public byte Variation;
+        [FieldOffset(31)] public ActionType ActionType;
+        [FieldOffset(33)] public byte TargetCount;
+    }
+
+    [UsedImplicitly(ImplicitUseTargetFlags.Members)]
+    private enum EActionEffectType : byte
+    {
+        None = 0,
+        Miss = 1,
+        FullResist = 2,
+        Damage = 3,
+        Heal = 4,
+        BlockedDamage = 5,
+        ParriedDamage = 6,
+        Invulnerable = 7,
+        NoEffectText = 8,
+        Unknown0 = 9,
+        MpLoss = 10,
+        MpGain = 11,
+        TpLoss = 12,
+        TpGain = 13,
+        ApplyStatusEffectTarget = 14,
+        ApplyStatusEffectSource = 15,
+        RecoveredFromStatusEffect = 16,
+        LoseStatusEffectTarget = 17,
+        LoseStatusEffectSource = 18,
+        StatusNoEffect = 20,
+        ThreatPosition = 24,
+        EnmityAmountUp = 25,
+        EnmityAmountDown = 26,
+        StartActionCombo = 27,
+        ComboSucceed = 28,
+        Retaliation = 29,
+        Knockback = 32,
+        Attract1 = 33, //Here is an issue bout knockback. some is 32 some is 33.
+        Attract2 = 34,
+        Mount = 40,
+        FullResistStatus = 52,
+        FullResistStatus2 = 55,
+        VFX = 59,
+        Gauge = 60,
+        JobGauge = 61,
+        SetModelState = 72,
+        SetHP = 73,
+        PartialInvulnerable = 74,
+        Interrupt = 75,
+    }
+}
diff --git a/Questionable/Controller/MiniTaskController.cs b/Questionable/Controller/MiniTaskController.cs
index 6055a68c6..c9aec197c 100644
--- a/Questionable/Controller/MiniTaskController.cs
+++ b/Questionable/Controller/MiniTaskController.cs
@@ -17,26 +17,29 @@ using Mount = Questionable.Controller.Steps.Common.Mount;
 
 namespace Questionable.Controller;
 
-internal abstract class MiniTaskController<T>
+internal abstract class MiniTaskController<T> : IDisposable
 {
     protected readonly TaskQueue _taskQueue = new();
 
     private readonly IChatGui _chatGui;
     private readonly ICondition _condition;
     private readonly IServiceProvider _serviceProvider;
+    private readonly InterruptHandler _interruptHandler;
     private readonly ILogger<T> _logger;
 
     private readonly string _actionCanceledText;
 
     protected MiniTaskController(IChatGui chatGui, ICondition condition, IServiceProvider serviceProvider,
-        IDataManager dataManager, ILogger<T> logger)
+        InterruptHandler interruptHandler, IDataManager dataManager, ILogger<T> logger)
     {
         _chatGui = chatGui;
         _logger = logger;
         _serviceProvider = serviceProvider;
+        _interruptHandler = interruptHandler;
         _condition = condition;
 
         _actionCanceledText = dataManager.GetString<LogMessage>(1314, x => x.Text)!;
+        _interruptHandler.Interrupted += HandleInterruption;
     }
 
     protected virtual void UpdateCurrentTask()
@@ -198,8 +201,21 @@ internal abstract class MiniTaskController<T>
         if (!isHandled)
         {
             if (GameFunctions.GameStringEquals(_actionCanceledText, message.TextValue) &&
-                !_condition[ConditionFlag.InFlight])
+                !_condition[ConditionFlag.InFlight] &&
+                _taskQueue.CurrentTaskExecutor?.ShouldInterruptOnDamage() == true)
                 InterruptQueueWithCombat();
         }
     }
+
+    protected virtual void HandleInterruption(object? sender, EventArgs e)
+    {
+        if (!_condition[ConditionFlag.InFlight] &&
+            _taskQueue.CurrentTaskExecutor?.ShouldInterruptOnDamage() == true)
+            InterruptQueueWithCombat();
+    }
+
+    public virtual void Dispose()
+    {
+        _interruptHandler.Interrupted -= HandleInterruption;
+    }
 }
diff --git a/Questionable/Controller/QuestController.cs b/Questionable/Controller/QuestController.cs
index 9d17fed1d..d514f267b 100644
--- a/Questionable/Controller/QuestController.cs
+++ b/Questionable/Controller/QuestController.cs
@@ -75,8 +75,9 @@ internal sealed class QuestController : MiniTaskController<QuestController>, IDi
         YesAlreadyIpc yesAlreadyIpc,
         TaskCreator taskCreator,
         IServiceProvider serviceProvider,
+        InterruptHandler interruptHandler,
         IDataManager dataManager)
-        : base(chatGui, condition, serviceProvider, dataManager, logger)
+        : base(chatGui, condition, serviceProvider, interruptHandler, dataManager, logger)
     {
         _clientState = clientState;
         _gameFunctions = gameFunctions;
@@ -801,11 +802,23 @@ internal sealed class QuestController : MiniTaskController<QuestController>, IDi
         _gatheringController.OnNormalToast(message);
     }
 
-    public void Dispose()
+    protected override void HandleInterruption(object? sender, EventArgs e)
+    {
+        if (!IsRunning)
+            return;
+
+        if (AutomationType == EAutomationType.Manual)
+            return;
+
+        base.HandleInterruption(sender, e);
+    }
+
+    public override void Dispose()
     {
         _toastGui.ErrorToast -= OnErrorToast;
         _toastGui.Toast -= OnNormalToast;
         _condition.ConditionChange -= OnConditionChange;
+        base.Dispose();
     }
 
     public sealed record StepProgress(
diff --git a/Questionable/Controller/Steps/Common/Mount.cs b/Questionable/Controller/Steps/Common/Mount.cs
index 1e03d8e97..4f35f4d97 100644
--- a/Questionable/Controller/Steps/Common/Mount.cs
+++ b/Questionable/Controller/Steps/Common/Mount.cs
@@ -110,6 +110,8 @@ internal static class Mount
                 ? ETaskResult.TaskComplete
                 : ETaskResult.StillRunning;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 
     internal enum MountResult
@@ -197,6 +199,8 @@ internal static class Mount
 
             return false;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 
     public enum EMountIf
diff --git a/Questionable/Controller/Steps/Common/NextQuest.cs b/Questionable/Controller/Steps/Common/NextQuest.cs
index 7f4b02614..3ac7758d7 100644
--- a/Questionable/Controller/Steps/Common/NextQuest.cs
+++ b/Questionable/Controller/Steps/Common/NextQuest.cs
@@ -61,5 +61,7 @@ internal static class NextQuest
         }
 
         public override ETaskResult Update() => ETaskResult.TaskComplete;
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 }
diff --git a/Questionable/Controller/Steps/Common/SendNotification.cs b/Questionable/Controller/Steps/Common/SendNotification.cs
index cf116028e..6d8bbcec6 100644
--- a/Questionable/Controller/Steps/Common/SendNotification.cs
+++ b/Questionable/Controller/Steps/Common/SendNotification.cs
@@ -104,5 +104,7 @@ internal static class SendNotification
         }
 
         public override ETaskResult Update() => ETaskResult.TaskComplete;
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 }
diff --git a/Questionable/Controller/Steps/Common/WaitConditionTask.cs b/Questionable/Controller/Steps/Common/WaitConditionTask.cs
index 367fdfec0..8203c0568 100644
--- a/Questionable/Controller/Steps/Common/WaitConditionTask.cs
+++ b/Questionable/Controller/Steps/Common/WaitConditionTask.cs
@@ -25,5 +25,7 @@ internal static class WaitCondition
 
             return DateTime.Now >= _continueAt ? ETaskResult.TaskComplete : ETaskResult.StillRunning;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 }
diff --git a/Questionable/Controller/Steps/Gathering/DoGather.cs b/Questionable/Controller/Steps/Gathering/DoGather.cs
index 0f4c8c7f2..bf4ab4aa9 100644
--- a/Questionable/Controller/Steps/Gathering/DoGather.cs
+++ b/Questionable/Controller/Steps/Gathering/DoGather.cs
@@ -236,6 +236,8 @@ internal static class DoGather
             EAction action = PickAction(minerAction, botanistAction);
             return ActionManager.Instance()->GetActionStatus(ActionType.Action, (uint)action) == 0;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 
     [SuppressMessage("ReSharper", "NotAccessedPositionalProperty.Local")]
diff --git a/Questionable/Controller/Steps/Gathering/DoGatherCollectable.cs b/Questionable/Controller/Steps/Gathering/DoGatherCollectable.cs
index 2b91f3538..fcd5efadf 100644
--- a/Questionable/Controller/Steps/Gathering/DoGatherCollectable.cs
+++ b/Questionable/Controller/Steps/Gathering/DoGatherCollectable.cs
@@ -198,6 +198,8 @@ internal static class DoGatherCollectable
             else
                 return botanistAction;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 
     [SuppressMessage("ReSharper", "NotAccessedPositionalProperty.Local")]
diff --git a/Questionable/Controller/Steps/Gathering/MoveToLandingLocation.cs b/Questionable/Controller/Steps/Gathering/MoveToLandingLocation.cs
index 38fa30cd6..bc183013e 100644
--- a/Questionable/Controller/Steps/Gathering/MoveToLandingLocation.cs
+++ b/Questionable/Controller/Steps/Gathering/MoveToLandingLocation.cs
@@ -59,5 +59,6 @@ internal static class MoveToLandingLocation
 
         public override ETaskResult Update() => moveExecutor.Update();
         public bool OnErrorToast(SeString message) => moveExecutor.OnErrorToast(message);
+        public override bool ShouldInterruptOnDamage() => moveExecutor.ShouldInterruptOnDamage();
     }
 }
diff --git a/Questionable/Controller/Steps/Gathering/TurnInDelivery.cs b/Questionable/Controller/Steps/Gathering/TurnInDelivery.cs
index caf2b0f46..0483605bd 100644
--- a/Questionable/Controller/Steps/Gathering/TurnInDelivery.cs
+++ b/Questionable/Controller/Steps/Gathering/TurnInDelivery.cs
@@ -80,5 +80,8 @@ internal static class TurnInDelivery
             addon->FireCallback(2, pickGatheringItem);
             return ETaskResult.StillRunning;
         }
+
+        // not even sure if any turn-in npcs are NEAR mobs; but we also need to be on a gathering/crafting job
+        public override bool ShouldInterruptOnDamage() => false;
     }
 }
diff --git a/Questionable/Controller/Steps/Interactions/Action.cs b/Questionable/Controller/Steps/Interactions/Action.cs
index 7255fa0bf..f7f975020 100644
--- a/Questionable/Controller/Steps/Interactions/Action.cs
+++ b/Questionable/Controller/Steps/Interactions/Action.cs
@@ -124,6 +124,8 @@ internal static class Action
 
             return ETaskResult.TaskComplete;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 
     internal sealed record UseMudraOnObject(uint DataId, EAction Action) : ITask
@@ -187,5 +189,7 @@ internal static class Action
             logger.LogError("Unable to find relevant combo for {Action}", Task.Action);
             return ETaskResult.TaskComplete;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 }
diff --git a/Questionable/Controller/Steps/Interactions/AetherCurrent.cs b/Questionable/Controller/Steps/Interactions/AetherCurrent.cs
index b244bbea9..7632e5749 100644
--- a/Questionable/Controller/Steps/Interactions/AetherCurrent.cs
+++ b/Questionable/Controller/Steps/Interactions/AetherCurrent.cs
@@ -65,5 +65,7 @@ internal static class AetherCurrent
             gameFunctions.IsAetherCurrentUnlocked(Task.AetherCurrentId)
                 ? ETaskResult.TaskComplete
                 : ETaskResult.StillRunning;
+
+        public override bool ShouldInterruptOnDamage() => true;
     }
 }
diff --git a/Questionable/Controller/Steps/Interactions/AethernetShard.cs b/Questionable/Controller/Steps/Interactions/AethernetShard.cs
index b1af7fe9a..db2c5212a 100644
--- a/Questionable/Controller/Steps/Interactions/AethernetShard.cs
+++ b/Questionable/Controller/Steps/Interactions/AethernetShard.cs
@@ -53,5 +53,7 @@ internal static class AethernetShard
             aetheryteFunctions.IsAetheryteUnlocked(Task.AetheryteLocation)
                 ? ETaskResult.TaskComplete
                 : ETaskResult.StillRunning;
+
+        public override bool ShouldInterruptOnDamage() => true;
     }
 }
diff --git a/Questionable/Controller/Steps/Interactions/Aetheryte.cs b/Questionable/Controller/Steps/Interactions/Aetheryte.cs
index d97547763..dd40fc69d 100644
--- a/Questionable/Controller/Steps/Interactions/Aetheryte.cs
+++ b/Questionable/Controller/Steps/Interactions/Aetheryte.cs
@@ -52,5 +52,7 @@ internal static class Aetheryte
             aetheryteFunctions.IsAetheryteUnlocked(Task.AetheryteLocation)
                 ? ETaskResult.TaskComplete
                 : ETaskResult.StillRunning;
+
+        public override bool ShouldInterruptOnDamage() => true;
     }
 }
diff --git a/Questionable/Controller/Steps/Interactions/Combat.cs b/Questionable/Controller/Steps/Interactions/Combat.cs
index 463c32cec..e63058a81 100644
--- a/Questionable/Controller/Steps/Interactions/Combat.cs
+++ b/Questionable/Controller/Steps/Interactions/Combat.cs
@@ -190,5 +190,7 @@ internal static class Combat
                 return ETaskResult.TaskComplete;
             }
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 }
diff --git a/Questionable/Controller/Steps/Interactions/Dive.cs b/Questionable/Controller/Steps/Interactions/Dive.cs
index b5389774c..eea9cd871 100644
--- a/Questionable/Controller/Steps/Interactions/Dive.cs
+++ b/Questionable/Controller/Steps/Interactions/Dive.cs
@@ -71,6 +71,8 @@ internal static class Dive
             return base.Update();
         }
 
+        public override bool ShouldInterruptOnDamage() => false;
+
         protected override ETaskResult UpdateInternal()
         {
             if (condition[ConditionFlag.Diving])
diff --git a/Questionable/Controller/Steps/Interactions/Duty.cs b/Questionable/Controller/Steps/Interactions/Duty.cs
index 5e20accf0..b59f8ce70 100644
--- a/Questionable/Controller/Steps/Interactions/Duty.cs
+++ b/Questionable/Controller/Steps/Interactions/Duty.cs
@@ -93,6 +93,8 @@ internal static class Duty
                 ? ETaskResult.TaskComplete
                 : ETaskResult.StillRunning;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 
     internal sealed record WaitAutoDutyTask(uint ContentFinderConditionId) : ITask
@@ -117,6 +119,8 @@ internal static class Duty
                 ? ETaskResult.TaskComplete
                 : ETaskResult.StillRunning;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 
     internal sealed record OpenDutyFinderTask(uint ContentFinderConditionId) : ITask
@@ -138,5 +142,7 @@ internal static class Duty
         }
 
         public override ETaskResult Update() => ETaskResult.TaskComplete;
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 }
diff --git a/Questionable/Controller/Steps/Interactions/Emote.cs b/Questionable/Controller/Steps/Interactions/Emote.cs
index 085b0356e..d6dd71467 100644
--- a/Questionable/Controller/Steps/Interactions/Emote.cs
+++ b/Questionable/Controller/Steps/Interactions/Emote.cs
@@ -51,6 +51,8 @@ internal static class Emote
             chatFunctions.UseEmote(Task.DataId, Task.Emote);
             return true;
         }
+
+        public override bool ShouldInterruptOnDamage() => true;
     }
 
     internal sealed record UseOnSelf(EEmote Emote) : ITask
@@ -65,5 +67,7 @@ internal static class Emote
             chatFunctions.UseEmote(Task.Emote);
             return true;
         }
+
+        public override bool ShouldInterruptOnDamage() => true;
     }
 }
diff --git a/Questionable/Controller/Steps/Interactions/EquipItem.cs b/Questionable/Controller/Steps/Interactions/EquipItem.cs
index f5cd4e111..d761926d6 100644
--- a/Questionable/Controller/Steps/Interactions/EquipItem.cs
+++ b/Questionable/Controller/Steps/Interactions/EquipItem.cs
@@ -183,5 +183,7 @@ internal static class EquipItem
 
             return false;
         }
+
+        public override bool ShouldInterruptOnDamage() => true;
     }
 }
diff --git a/Questionable/Controller/Steps/Interactions/EquipRecommended.cs b/Questionable/Controller/Steps/Interactions/EquipRecommended.cs
index 3b2be0f17..295bb8cc1 100644
--- a/Questionable/Controller/Steps/Interactions/EquipRecommended.cs
+++ b/Questionable/Controller/Steps/Interactions/EquipRecommended.cs
@@ -98,5 +98,7 @@ internal static class EquipRecommended
 
             return ETaskResult.TaskComplete;
         }
+
+        public override bool ShouldInterruptOnDamage() => true;
     }
 }
diff --git a/Questionable/Controller/Steps/Interactions/Interact.cs b/Questionable/Controller/Steps/Interactions/Interact.cs
index 0ca70c11f..0073349b3 100644
--- a/Questionable/Controller/Steps/Interactions/Interact.cs
+++ b/Questionable/Controller/Steps/Interactions/Interact.cs
@@ -228,6 +228,8 @@ internal static class Interact
             }
         }
 
+        public override bool ShouldInterruptOnDamage() => true;
+
         private enum EInteractionState
         {
             None,
diff --git a/Questionable/Controller/Steps/Interactions/Jump.cs b/Questionable/Controller/Steps/Interactions/Jump.cs
index f7b9892d5..3238405c8 100644
--- a/Questionable/Controller/Steps/Interactions/Jump.cs
+++ b/Questionable/Controller/Steps/Interactions/Jump.cs
@@ -80,6 +80,8 @@ internal static class Jump
 
             return ETaskResult.TaskComplete;
         }
+
+        public override bool ShouldInterruptOnDamage() => true;
     }
 
     internal sealed class DoSingleJump(
diff --git a/Questionable/Controller/Steps/Interactions/Say.cs b/Questionable/Controller/Steps/Interactions/Say.cs
index f13ab4ab9..ffb562152 100644
--- a/Questionable/Controller/Steps/Interactions/Say.cs
+++ b/Questionable/Controller/Steps/Interactions/Say.cs
@@ -48,5 +48,7 @@ internal static class Say
             chatFunctions.ExecuteCommand($"/say {Task.ChatMessage}");
             return true;
         }
+
+        public override bool ShouldInterruptOnDamage() => true;
     }
 }
diff --git a/Questionable/Controller/Steps/Interactions/StatusOff.cs b/Questionable/Controller/Steps/Interactions/StatusOff.cs
index 746f7394c..c9b2b4ca3 100644
--- a/Questionable/Controller/Steps/Interactions/StatusOff.cs
+++ b/Questionable/Controller/Steps/Interactions/StatusOff.cs
@@ -43,5 +43,7 @@ internal static class StatusOff
         {
             return gameFunctions.HasStatus(Task.Status) ? ETaskResult.StillRunning : ETaskResult.TaskComplete;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 }
diff --git a/Questionable/Controller/Steps/Interactions/UseItem.cs b/Questionable/Controller/Steps/Interactions/UseItem.cs
index bf779a024..abc427ad4 100644
--- a/Questionable/Controller/Steps/Interactions/UseItem.cs
+++ b/Questionable/Controller/Steps/Interactions/UseItem.cs
@@ -205,6 +205,8 @@ internal static class UseItem
             else
                 return TimeSpan.FromSeconds(5);
         }
+
+        public override bool ShouldInterruptOnDamage() => true;
     }
 
     internal sealed record UseOnGround(
diff --git a/Questionable/Controller/Steps/Leves/InitiateLeve.cs b/Questionable/Controller/Steps/Leves/InitiateLeve.cs
index ab584cefc..31cf47055 100644
--- a/Questionable/Controller/Steps/Leves/InitiateLeve.cs
+++ b/Questionable/Controller/Steps/Leves/InitiateLeve.cs
@@ -50,6 +50,8 @@ internal static class InitiateLeve
 
             return ETaskResult.TaskComplete;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 
     internal sealed record OpenJournal(ElementId ElementId) : ITask
@@ -85,6 +87,8 @@ internal static class InitiateLeve
 
             return ETaskResult.StillRunning;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 
     internal sealed record Initiate(ElementId ElementId) : ITask
@@ -111,6 +115,8 @@ internal static class InitiateLeve
 
             return ETaskResult.StillRunning;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 
     internal sealed class SelectDifficulty : ITask
@@ -138,5 +144,7 @@ internal static class InitiateLeve
 
             return ETaskResult.StillRunning;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 }
diff --git a/Questionable/Controller/Steps/Shared/AethernetShortcut.cs b/Questionable/Controller/Steps/Shared/AethernetShortcut.cs
index e63b69c0a..460aa440b 100644
--- a/Questionable/Controller/Steps/Shared/AethernetShortcut.cs
+++ b/Questionable/Controller/Steps/Shared/AethernetShortcut.cs
@@ -269,5 +269,7 @@ internal static class AethernetShortcut
 
             return ETaskResult.TaskComplete;
         }
+
+        public override bool ShouldInterruptOnDamage() => true;
     }
 }
diff --git a/Questionable/Controller/Steps/Shared/AetheryteShortcut.cs b/Questionable/Controller/Steps/Shared/AetheryteShortcut.cs
index af5753334..b2748b183 100644
--- a/Questionable/Controller/Steps/Shared/AetheryteShortcut.cs
+++ b/Questionable/Controller/Steps/Shared/AetheryteShortcut.cs
@@ -221,6 +221,8 @@ internal static class AetheryteShortcut
         }
 
         public override bool WasInterrupted() => condition[ConditionFlag.InCombat] || base.WasInterrupted();
+
+        public override bool ShouldInterruptOnDamage() => true;
     }
 
     internal sealed record MoveAwayFromAetheryte(EAetheryteLocation TargetAetheryte) : ITask
@@ -264,5 +266,7 @@ internal static class AetheryteShortcut
         }
 
         public override ETaskResult Update() => moveExecutor.Update();
+
+        public override bool ShouldInterruptOnDamage() => true;
     }
 }
diff --git a/Questionable/Controller/Steps/Shared/Craft.cs b/Questionable/Controller/Steps/Shared/Craft.cs
index 26493ca02..d986368f6 100644
--- a/Questionable/Controller/Steps/Shared/Craft.cs
+++ b/Questionable/Controller/Steps/Shared/Craft.cs
@@ -133,5 +133,8 @@ internal static class Craft
             return inventoryManager->GetInventoryItemCount(Task.ItemId, isHq: false, checkEquipped: false)
                    + inventoryManager->GetInventoryItemCount(Task.ItemId, isHq: true, checkEquipped: false);
         }
+
+        // we're on a crafting class, so combat doesn't make much sense (we also can't change classes in combat...)
+        public override bool ShouldInterruptOnDamage() => false;
     }
 }
diff --git a/Questionable/Controller/Steps/Shared/Gather.cs b/Questionable/Controller/Steps/Shared/Gather.cs
index 73dd8d12f..f4aad9c94 100644
--- a/Questionable/Controller/Steps/Shared/Gather.cs
+++ b/Questionable/Controller/Steps/Shared/Gather.cs
@@ -100,6 +100,8 @@ internal static class Gather
                        minCollectability: (short)itemToGather.Collectability) >=
                    itemToGather.ItemCount;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 
     internal sealed record GatheringTask(
@@ -140,6 +142,9 @@ internal static class Gather
             gatheringController.OnErrorToast(ref message, ref isHandled);
             return isHandled;
         }
+
+        // we're on a gathering class, so combat doesn't make much sense (we also can't change classes in combat...)
+        public override bool ShouldInterruptOnDamage() => false;
     }
 
     /// <summary>
@@ -154,5 +159,7 @@ internal static class Gather
     {
         protected override bool Start() => true;
         public override ETaskResult Update() => ETaskResult.TaskComplete;
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 }
diff --git a/Questionable/Controller/Steps/Shared/MoveTo.cs b/Questionable/Controller/Steps/Shared/MoveTo.cs
index 4df2870fa..656891080 100644
--- a/Questionable/Controller/Steps/Shared/MoveTo.cs
+++ b/Questionable/Controller/Steps/Shared/MoveTo.cs
@@ -286,6 +286,8 @@ internal static class MoveTo
             return base.WasInterrupted();
         }
 
+        public override bool ShouldInterruptOnDamage() => false;
+
         public bool OnErrorToast(SeString message)
         {
             if (GameFunctions.GameStringEquals(_cannotExecuteAtThisTime, message.TextValue))
@@ -302,6 +304,8 @@ internal static class MoveTo
         protected override bool Start() => true;
 
         public override ETaskResult Update() => ETaskResult.TaskComplete;
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 
     internal sealed record MoveTask(
@@ -361,6 +365,8 @@ internal static class MoveTo
 
             return ETaskResult.TaskComplete;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 
     internal sealed class LandTask : ITask
@@ -421,5 +427,7 @@ internal static class MoveTo
 
             return false;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 }
diff --git a/Questionable/Controller/Steps/Shared/RedeemRewardItems.cs b/Questionable/Controller/Steps/Shared/RedeemRewardItems.cs
index 408b92f7e..c7abcae7b 100644
--- a/Questionable/Controller/Steps/Shared/RedeemRewardItems.cs
+++ b/Questionable/Controller/Steps/Shared/RedeemRewardItems.cs
@@ -74,5 +74,7 @@ internal static class RedeemRewardItems
 
             return DateTime.Now <= _continueAt ? ETaskResult.StillRunning : ETaskResult.TaskComplete;
         }
+
+        public override bool ShouldInterruptOnDamage() => true;
     }
 }
diff --git a/Questionable/Controller/Steps/Shared/SkipCondition.cs b/Questionable/Controller/Steps/Shared/SkipCondition.cs
index 5abab0594..ebf1dc1f2 100644
--- a/Questionable/Controller/Steps/Shared/SkipCondition.cs
+++ b/Questionable/Controller/Steps/Shared/SkipCondition.cs
@@ -315,5 +315,7 @@ internal static class SkipCondition
         }
 
         public override ETaskResult Update() => ETaskResult.SkipRemainingTasksForStep;
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 }
diff --git a/Questionable/Controller/Steps/Shared/StepDisabled.cs b/Questionable/Controller/Steps/Shared/StepDisabled.cs
index f70653592..de58cac36 100644
--- a/Questionable/Controller/Steps/Shared/StepDisabled.cs
+++ b/Questionable/Controller/Steps/Shared/StepDisabled.cs
@@ -31,5 +31,7 @@ internal static class StepDisabled
             logger.LogInformation("Skipping step, as it is disabled");
             return ETaskResult.SkipRemainingTasksForStep;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 }
diff --git a/Questionable/Controller/Steps/Shared/SwitchClassJob.cs b/Questionable/Controller/Steps/Shared/SwitchClassJob.cs
index 59477feca..18bfef7e5 100644
--- a/Questionable/Controller/Steps/Shared/SwitchClassJob.cs
+++ b/Questionable/Controller/Steps/Shared/SwitchClassJob.cs
@@ -52,5 +52,8 @@ internal static class SwitchClassJob
         }
 
         protected override ETaskResult UpdateInternal() => ETaskResult.TaskComplete;
+
+        // can we even take damage while switching jobs? we should be out of combat...
+        public override bool ShouldInterruptOnDamage() => false;
     }
 }
diff --git a/Questionable/Controller/Steps/Shared/WaitAtEnd.cs b/Questionable/Controller/Steps/Shared/WaitAtEnd.cs
index 0b3a02ba9..d39c7c2a3 100644
--- a/Questionable/Controller/Steps/Shared/WaitAtEnd.cs
+++ b/Questionable/Controller/Steps/Shared/WaitAtEnd.cs
@@ -157,6 +157,8 @@ internal static class WaitAtEnd
             Delay = Task.Delay;
             return true;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 
     internal sealed class WaitNextStepOrSequence : ITask
@@ -169,6 +171,8 @@ internal static class WaitAtEnd
         protected override bool Start() => true;
 
         public override ETaskResult Update() => ETaskResult.StillRunning;
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 
     internal sealed record WaitForCompletionFlags(QuestId Quest, QuestStep Step) : ITask
@@ -190,6 +194,8 @@ internal static class WaitAtEnd
                 ? ETaskResult.TaskComplete
                 : ETaskResult.StillRunning;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 
     internal sealed record WaitObjectAtPosition(
@@ -209,6 +215,8 @@ internal static class WaitAtEnd
             gameFunctions.IsObjectAtPosition(Task.DataId, Task.Destination, Task.Distance)
                 ? ETaskResult.TaskComplete
                 : ETaskResult.StillRunning;
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 
     internal sealed record WaitQuestAccepted(ElementId ElementId) : ITask
@@ -226,6 +234,8 @@ internal static class WaitAtEnd
                 ? ETaskResult.TaskComplete
                 : ETaskResult.StillRunning;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 
     internal sealed record WaitQuestCompleted(ElementId ElementId) : ITask
@@ -241,6 +251,8 @@ internal static class WaitAtEnd
         {
             return questFunctions.IsQuestComplete(Task.ElementId) ? ETaskResult.TaskComplete : ETaskResult.StillRunning;
         }
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 
     internal sealed record NextStep(ElementId ElementId, int Sequence) : ILastTask
@@ -253,6 +265,8 @@ internal static class WaitAtEnd
         protected override bool Start() => true;
 
         public override ETaskResult Update() => ETaskResult.NextStep;
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 
     internal sealed class EndAutomation : ILastTask
@@ -268,5 +282,7 @@ internal static class WaitAtEnd
         protected override bool Start() => true;
 
         public override ETaskResult Update() => ETaskResult.End;
+
+        public override bool ShouldInterruptOnDamage() => false;
     }
 }
diff --git a/Questionable/Controller/Steps/Shared/WaitAtStart.cs b/Questionable/Controller/Steps/Shared/WaitAtStart.cs
index c2c304b45..8386e6365 100644
--- a/Questionable/Controller/Steps/Shared/WaitAtStart.cs
+++ b/Questionable/Controller/Steps/Shared/WaitAtStart.cs
@@ -31,6 +31,7 @@ internal static class WaitAtStart
             Delay = Task.Delay;
             return true;
         }
-    }
 
+        public override bool ShouldInterruptOnDamage() => false;
+    }
 }
diff --git a/Questionable/Controller/Steps/TaskExecutor.cs b/Questionable/Controller/Steps/TaskExecutor.cs
index d0315dbcd..30e10b643 100644
--- a/Questionable/Controller/Steps/TaskExecutor.cs
+++ b/Questionable/Controller/Steps/TaskExecutor.cs
@@ -13,6 +13,8 @@ internal interface ITaskExecutor
 
     bool Start(ITask task);
 
+    bool ShouldInterruptOnDamage();
+
     bool WasInterrupted();
 
     ETaskResult Update();
@@ -56,4 +58,6 @@ internal abstract class TaskExecutor<T> : ITaskExecutor
     }
 
     public abstract ETaskResult Update();
+
+    public abstract bool ShouldInterruptOnDamage();
 }
diff --git a/Questionable/QuestionablePlugin.cs b/Questionable/QuestionablePlugin.cs
index a4b5bae9e..e0794c2d7 100644
--- a/Questionable/QuestionablePlugin.cs
+++ b/Questionable/QuestionablePlugin.cs
@@ -247,6 +247,7 @@ public sealed class QuestionablePlugin : IDalamudPlugin
         serviceCollection.AddSingleton<GatheringController>();
         serviceCollection.AddSingleton<ContextMenuController>();
         serviceCollection.AddSingleton<ShopController>();
+        serviceCollection.AddSingleton<InterruptHandler>();
 
         serviceCollection.AddSingleton<CraftworksSupplyController>();
         serviceCollection.AddSingleton<CreditsController>();