diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java
index 840cd92abd..67675b83c5 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/Microbot.java
@@ -464,6 +464,9 @@ public static void showMessage(String message) {
} else {
SwingUtilities.invokeAndWait(messageRunnable);
}
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ log.debug("Interrupted while displaying message {}", message);
} catch (Exception ex) {
log.error("Error displaying message {}:", message, ex);
}
@@ -487,6 +490,9 @@ public static void showMessage(String message, int disposeTime) {
} else {
SwingUtilities.invokeAndWait(messageRunnable);
}
+ } catch (InterruptedException ex) {
+ Thread.currentThread().interrupt();
+ log.debug("Interrupted while displaying message {}", message);
} catch (Exception ex) {
log.error("Error displaying message {}:", message, ex);
}
@@ -714,7 +720,9 @@ public static void log(String message, Level level, Exception ex) {
log.warn(message);
break;
case ERROR:
- if (ex != null) {
+ if (isInterruption(ex)) {
+ log.debug(message, ex);
+ } else if (ex != null) {
log.error(message, ex);
} else {
log.error(message);
@@ -729,6 +737,16 @@ public static void log(String message, Level level, Exception ex) {
}
}
+ static boolean isInterruption(Throwable ex) {
+ Throwable t = ex;
+ for (int depth = 0; t != null && depth < 64; depth++, t = t.getCause() == t ? null : t.getCause()) {
+ if (t instanceof InterruptedException) {
+ return true;
+ }
+ }
+ return false;
+ }
+
/**
* Opens a pop‑up interface with the specified title and description.
*
diff --git a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/diagnostics/ScriptErrorReporter.java b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/diagnostics/ScriptErrorReporter.java
index d9a37b78c2..b328274e18 100644
--- a/runelite-client/src/main/java/net/runelite/client/plugins/microbot/diagnostics/ScriptErrorReporter.java
+++ b/runelite-client/src/main/java/net/runelite/client/plugins/microbot/diagnostics/ScriptErrorReporter.java
@@ -89,6 +89,10 @@ protected void append(ILoggingEvent event)
}
IThrowableProxy root = rootCause(event.getThrowableProxy());
+ if (root != null && InterruptedException.class.getName().equals(root.getClassName()))
+ {
+ return;
+ }
List frames = frames(root);
String fingerprint = root == null
? event.getLoggerName() + "|" + scrub(event.getMessage()).replaceAll("\\d+", "#")
diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/microbot/MicrobotLogInterruptTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/microbot/MicrobotLogInterruptTest.java
new file mode 100644
index 0000000000..a6d7c47f13
--- /dev/null
+++ b/runelite-client/src/test/java/net/runelite/client/plugins/microbot/MicrobotLogInterruptTest.java
@@ -0,0 +1,65 @@
+package net.runelite.client.plugins.microbot;
+
+import ch.qos.logback.classic.Level;
+import ch.qos.logback.classic.Logger;
+import ch.qos.logback.classic.spi.ILoggingEvent;
+import ch.qos.logback.core.read.ListAppender;
+import org.junit.After;
+import org.junit.Before;
+import org.junit.Test;
+import org.slf4j.LoggerFactory;
+import static org.junit.Assert.assertEquals;
+import static org.junit.Assert.assertFalse;
+
+public class MicrobotLogInterruptTest
+{
+ private Logger logger;
+ private ListAppender appender;
+ private Level previousLevel;
+
+ @Before
+ public void setUp()
+ {
+ logger = (Logger) LoggerFactory.getLogger(Microbot.class);
+ previousLevel = logger.getLevel();
+ logger.setLevel(Level.DEBUG);
+ appender = new ListAppender<>();
+ appender.start();
+ logger.addAppender(appender);
+ }
+
+ @After
+ public void tearDown()
+ {
+ logger.detachAppender(appender);
+ logger.setLevel(previousLevel);
+ }
+
+ @Test
+ public void interruptedClientThreadWaitIsNotLoggedAsError()
+ {
+ Microbot.logStackTrace("AttackNpcScript", new RuntimeException("Interrupted waiting for client thread", new InterruptedException()));
+
+ assertEquals(1, appender.list.size());
+ assertEquals(Level.DEBUG, appender.list.get(0).getLevel());
+ }
+
+ @Test
+ public void otherFailuresStillLogError()
+ {
+ Microbot.logStackTrace("AttackNpcScript", new IllegalStateException("boom"));
+
+ assertEquals(1, appender.list.size());
+ assertEquals(Level.ERROR, appender.list.get(0).getLevel());
+ }
+
+ @Test
+ public void cyclicCauseChainTerminates()
+ {
+ IllegalStateException a = new IllegalStateException("a");
+ IllegalStateException b = new IllegalStateException("b", a);
+ a.initCause(b);
+
+ assertFalse(Microbot.isInterruption(a));
+ }
+}
diff --git a/runelite-client/src/test/java/net/runelite/client/plugins/microbot/diagnostics/ScriptErrorReporterTest.java b/runelite-client/src/test/java/net/runelite/client/plugins/microbot/diagnostics/ScriptErrorReporterTest.java
index f524a8d64b..4b3cf86854 100644
--- a/runelite-client/src/test/java/net/runelite/client/plugins/microbot/diagnostics/ScriptErrorReporterTest.java
+++ b/runelite-client/src/test/java/net/runelite/client/plugins/microbot/diagnostics/ScriptErrorReporterTest.java
@@ -193,6 +193,14 @@ public void requeuesRejectedBatch()
assertEquals(2, captor.getValue().getAsJsonArray("errors").get(0).getAsJsonObject().get("count").getAsInt());
}
+ @Test
+ public void ignoresInterruptedWaits()
+ {
+ log(Level.ERROR, "AttackNpcScript", new RuntimeException("Interrupted waiting for client thread", new InterruptedException()));
+ log(Level.ERROR, "Error displaying message {}:", new InterruptedException());
+ assertEquals(0, reporter.pendingCount());
+ }
+
@Test
public void requeuesBatchWhenSubmitThrows()
{