Repository navigation
fix(ShootingStar): close stop race on null panel and null state (1.4.11) - #652
Merged
chsami merged 2 commits intoOct 17, 2026
Merged
Conversation
On "Oh dear, you are dead!" the plugin called its own shutDown() directly, which removed and nulled the panel while the plugin stayed registered, so tick, event and config handlers kept queueing updatePanelList() runnables that dereferenced the null panel on the EDT. Even a runnable queued just before a normal stop read the field after removePanel() had cleared it. The script loop null-checked the static state field and then switched on it again after several client calls, so a concurrent stop that set state to null threw on ordinal(). - stop through Microbot.stopPlugin(this) so the plugin is unregistered - skip updatePanelList when there is no panel, and re-read the panel on the EDT so queued updates skip a removed panel - read state into a local once per loop iteration and switch on it - treat a missing antiban category as not busy Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
ShootingStarScript.shutdown() nulled state, currentStar and pickaxe while mainScheduledFuture.cancel(true) does not wait for a running iteration, so a loop that had already passed its null check went on to dereference the cleared fields (switch on state, currentStar in the WALKING, MINING and BANKING cases). The previous local snapshot of state only moved the NPE to the currentStar reads. - stop clearing state, currentStar and pickaxe in shutdown() - clear them at the top of run() with the other per-run resets - drop the now unneeded state snapshot in the loop - read the static state once in the overlay before rendering it Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
3 tasks done
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Problem
Client error telemetry for ShootingStarPlugin 1.4.10 (client 2.6.33) recorded 793 NullPointerExceptions across three groups. All three come from stopping the plugin:
NullPointerExceptionon the AWT EDT inShootingStarPlugin.lambda$updatePanelList$3:419, wherepanel.updateList(stars)runs withpanelalready null.lambda$updatePanelList$4:423, frompanel.refreshList(stars).NullPointerExceptioninShootingStarScript.lambda$run$1:128onswitch (state), afterstatehad been set to null.Root cause
shutdownOnDeathwas on, the death handler calledshutDown()directly. That removed the panel and setpanelto null, but the plugin was still registered with the event bus. Later events and game ticks kept callingupdatePanelList, and each call queued aninvokeLaterlambda that dereferenced the nullpanelon the EDT. A runnable queued just before a normal stop could hit the same NPE.Script.shutdown()callsmainScheduledFuture.cancel(true), and that does not wait for an iteration that is already running.ShootingStarScript.shutdown()then setstate,currentStarandpickaxeto null, so the iteration still running read null fields atswitch (state). The same could happen withcurrentStarin WALKING, MINING and BANKING.Change
Microbot.stopPlugin(this), so the plugin is stopped and unregistered the normal way. Called off the EDT, this does not block.updatePanelListreturns early whenpanelis null. The EDT lambda readspanelinto a local once and skips the update if it is null.shutdown()no longer clearsstate,currentStarorpickaxe.run()resets them at startup instead, next to the other per-run fields it already resets.stateinto a local once before it null-checks and renders it.Rs2Antiban.getCategory().isBusy()goes through a null-safeisAntibanBusyhelper, becauseRs2Antiban.resetAntibanSettings()sets the category to null.Validation
ShootingStarStopRaceTest(5 tests). It covers a queued panel update after the panel is removed, which NPEs on the old lambdas. It also checks thatshutdown()leaves the loop state alone for an iteration that is still running, which fails on code that clears the state on stop../gradlew test --tests 'net.runelite.client.plugins.microbot.shootingstar.*' build -PpluginList=ShootingStarPluginpasses. That covers ShootingStarStopRaceTest (5), ShootingStarWalkBeforeHopTest (15) and ShootingStarWorldFilterTest (10).Known gaps
run()resettingstatecould still cause an NPE in that old iteration. The main loop catches and logs it, and it does no harm.Rs2Antiban.resetAntibanSettings()setting the category to null is a client issue. JewelryScript and GlassblowingScript callRs2Antiban.getCategory().isBusy()the same way and are not covered here. A client-side non-null default category would fix all of them.Rs2Bank.walkToBankNPE on a null bank location) arrived in the same upload. Client fix #1996 already covers it, so this PR does not touch it.🤖 Generated with Claude Code
https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV