Repository navigation
Release Microbot 2.6.34: telemetry-driven crash fixes - #2002
Merged
Merged
Conversation
chore: merge main (2.6.32) back into development
…oint processWalk cleared the target on reaching the path endpoint and let the same pass fall through to the after-stuck-check, which read the null currentTarget as an external cancel and returned EXIT even when the player stood on the goal. The endpoint branch now checks for a genuine cancel first, then returns ARRIVED when the player is within the tight finish threshold of the target (EXIT otherwise, with an honest trace). Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
fix(walker): report ARRIVED when a pass starts on the final path endpoint
…s them QuestManager.updateQuestList applies the SHOW_MEETS_REQS quest filter, which calls QuestHelper.clientMeetsRequirements, before every helper has had its requirements set up. CurrentAffairs.getGeneralRequirements then passed a null twoFreeInvSlots into List.of and the NPE aborted the quest list refresh on the client thread. Initialise requirements first, as SkillFiltering.questPassesSkillFilter already does. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…ffairs-npe fix(questhelper): initialise requirements before checking client meets them
getSceneObjects reads tiles from the local player's worldview and looped to Constants.SCENE_SIZE. On a sailing boat that worldview is only 8 or 16 tiles wide, so every scene-object query threw ArrayIndexOutOfBoundsException and aborted the walker. Take the loop bounds from the tile array instead. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…orldview-aioobe fix(gameobject): bound scene object scan by the worldview tile array
…ects AgentHandler.isClientDisconnect only matched English Linux/macOS socket error texts, so Windows WSAECONNABORTED/WSAECONNRESET and localized messages were logged at ERROR and a 500 was written to the dead socket, which then rethrew. Response body writes now go through writeBody, which wraps any IOException in ClientDisconnectException, and the Windows English texts are matched as well. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…isconnect-logged-as-error fix(agentserver): treat failed response body writes as client disconnects
Treat a null inventory setups list (MInventorySetupsPlugin not started) as an empty combo box without clearing the stored selection, and drop the synchronous sendConfig() calls that took the profile file lock on the EDT and threw OverlappingFileLockException when the scheduled save held it. setConfiguration already queues the change for the scheduled save. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…y-setups-combobox-crash fix(config-panel): stop inventory setups combo box crashing on the EDT
setDefaultConfiguration treated any key whose name contained "inventorysetup" as an InventorySetup and parsed its value as JSON. Boolean toggles such as Hub plugins' useInventorySetup store "true"/"false", so clicking Reset threw IllegalStateException on the EDT. Use the item's return type instead, and tolerate unparseable stored setups. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…ysetup-key-boolean fix(config): detect InventorySetup config items by return type on reset
Rs2GrandExchange.processOffer BUY passes getAvailableSlot() into getOfferBuyButton, which is null when every GE slot is occupied. getSlot then switched on the null enum and threw an NPE instead of letting the existing buyOffer null check abort the buy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…rnal LocalPoint.fromWorld returns null in an instance when the player's world point is on another plane or outside the top-level scene (boat worldview, scene loading). Fall back to the player's local location, then to the raw world location, instead of passing null into WorldPoint.fromLocalInstance. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…cation-null-localpoint fix(player): guard null LocalPoint in instanced getWorldLocation_Internal
POST /quest-helper/stop called QuestManager.shutDownQuest(true) on the AgentServer HTTP thread. With true it refreshes the quest list, which reads quest state via client.runScript and throws "must be called on client thread". Run it through runOnClientThreadOptional and return 500 when it does not complete. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…er-stop-offthread fix(agentserver): stop quest helper on the client thread
…op widget itemBounds dereferenced the shop items widget and indexed its dynamic children without checks, throwing NPE when the shop was closing and AIOOBE before the children were populated. Return null in those cases so invokeMenu falls back to its default rectangle, compute the bounds once, and have buyItem return false when an exception is caught. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
fix(shop): guard Rs2Shop.itemBounds against missing or unpopulated shop widget
NPC.getId resolves the transformed composition using varbits and the definition cache, which NPEs inside the client when called from script threads such as Forestry blocking-event validate() via withId/nearest. Marshal it through runOnClientThreadOptional like Rs2ActorModel.getName. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…thread-npe fix(npc): read Rs2NpcModel id on the client thread
fix(ge): stop buyItem NPE when no GE slot is free
getCollectedGroundItems iterated the synchronized table outside its lock, so script threads hit ConcurrentModificationException when the client thread spawned or despawned items during the copy. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…zed-table-copy-cme fix(grounditems): hold table lock while copying collected ground items
…olved getObjectComposition() replaced the composition with getImpostor() whenever impostor ids existed, returning null when the varbit/varp could not be resolved, so Rs2TileObjectModel.click NPE'd on objComp.getImpostorIds(). Keep the base composition in that case, drop click's redundant impostor re-check, and return false when no composition is available. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…idden getImpostor() returns null when the varbit/varp selects transform -1, meaning the object is currently hidden. click() previously fell back to the base composition and dispatched GAME_OBJECT_FIRST_OPTION on an invisible object, returning true. click() now resolves the impostor itself on the client thread and returns false before turning the camera or invoking the menu action, matching Rs2GameObject.clickObject. getObjectComposition() keeps its base-composition fallback for read callers, and getName() keeps returning null for hidden objects on purpose. The legacy util.tileobject.Rs2TileObjectModel shares the pattern but has no callers in the client or Microbot-Hub, so it is left unchanged. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…-impostor-composition fix(tileobject): handle unresolved impostor in Rs2TileObjectModel.click
…eToPath pathTo returns an empty list when Rs2Tile.pathTo yields no checkpoints or its first tile is null, so distanceToPath called get(-1) and threw IndexOutOfBoundsException, aborting AIOFighter's target sort. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…etopath-empty-path-ioobe fix(coords): treat empty path as unreachable in Rs2WorldPoint.distanceToPath
Route every Rs2Camera.angleToTile overload through one helper that returns the current camera facing when the local player world location is null (login, loading, hop), so turnTo and centerTileOnScreen become no-ops instead of throwing a NullPointerException on the client thread. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…-null-player-location fix(camera): keep Rs2Camera.angleToTile null-safe without a local player
…lures The loop, validate and execute catch blocks in BlockingEventManager logged through the format overload of Microbot.log, which stringified the exception into the message and called log.error without a throwable. Error telemetry therefore received these failures (e.g. KittenAttentionEvent RuntimeException) with no exception type or stack. Validate/execute now use the Exception overload, which attaches the throwable and demotes interrupt chains to DEBUG; the Throwable loop catch logs through slf4j with the throwable. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…-drops-throwable fix(blocking-events): attach throwable when logging BlockingEvent failures
… player getDistanceFromPlayer and isWithinDistanceFromPlayer dereferenced Client.getLocalPlayer() and the NPC LocalPoint inside the client-thread lambda, so calls made on the login screen, while hopping or during loading threw an NPE that ClientThread logged at ERROR. Return Integer.MAX_VALUE / false when the player, its location or the NPC location is missing, in both the api and legacy util Rs2NpcModel. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…null-local-player fix(npc): keep Rs2NpcModel distance helpers null-safe without a local player
…location is unknown The world-point distance fallback in Rs2GameObject.clickObject dereferenced Rs2Player.getWorldLocation() without a null check, so calls during login, world hop or loading (e.g. via Rs2Bank.openBank) threw an NPE. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…ject-null-player-location fix(gameobject): return false from clickObject when player or object location is unknown
Rs2Bank.walkToBank() passes getNearestBank() straight into walkToBank(BankLocation, boolean), which dereferenced the location even though getNearestBank() returns null without a player location or a reachable bank. Guard it the same way walkToBankAndUseBank already does. BankLocation.hasRequirements for MINING_GUILD read Rs2Player.getWorldLocation() twice without a null check, so a location lost mid-search (loading, hop, logout) threw during the nearest-bank filter. Read it once and treat a missing location as unmet. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…ll-bank-location fix(bank): return false from walkToBank when no bank location is known
… boxes Kourend banks and deposit boxes have no quest requirement in game, but hasRequirements excluded them until Client of Kourend was started. On such accounts Rs2Bank.getNearestBank skipped every Kourend bank, so scripts like Stall Thieving at the Hosidius fruit stall home-teleported to a bank outside Kourend instead of walking to the Vinery bank. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…us-bank-teleports-home fix(bank): drop Client of Kourend gate from Kourend banks and deposit boxes
…d of 500 Handlers cast JSON body fields directly, so a quoted value such as "quantity": "28" threw ClassCastException, returned 500 and was logged at error level. Add typed body accessors on AgentHandler that accept numeric and boolean strings and otherwise throw BadRequestException, use them in place of the raw casts, and map any remaining ClassCastException to a 400 logged at debug level. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…drop ClassCastException backstop Keyboard, Settings, WidgetClick and WidgetInvoke handlers now read body fields through getIntField/getStringField/getBooleanField, and wait, timeout and distance in Login/Logout/Walk handlers go through the same helpers. The blanket ClassCastException catch in AgentHandler.handle is removed so genuine internal cast bugs still report as 500 and reach error telemetry. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…d-type-classcast fix(agentserver): return 400 for wrong-typed JSON body fields instead of 500
The Web Walker clue panel timer, Start button and clue hotkey all call getCurrentClueLocation from the EDT. Clue location providers such as AnagramClue read varbits, which throws "must be called on client thread". Resolve the location through runOnClientThreadOptional, falling back to null. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…el-varbit-off-client-thread fix(shortestpath): resolve clue location on the client thread
…m slot Rs2Inventory.invokeMenu called Optional.orElseGet(null), which throws an NPE whenever the active inventory container has no child for the item's slot (seen while selling to a shop). Use orElse(null) so the existing fallback to the item's inventory actions is reached. Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
…nu-orelseget-null fix(inventory): avoid NPE in invokeMenu when no widget matches item slot
chore: merge main (2.6.33) back into development
Contributor
|
Important Review skippedToo many files! This PR contains 102 files, which is 2 over the limit of 100. To get a review, reduce the PR to 100 files or fewer by splitting it into smaller PRs or changing its base branch. Upgrade to a paid plan to raise the limit. This review couldn't start because sufficient usage credits or metered capacity aren't available. Add credits or update usage-based reviews in the billing tab, then retry. ⚙️ Run configuration
📒 Files selected for processing: 102
You can disable this status message by setting the
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
This was referenced Oct 18, 2026
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.
Summary
Promotes
developmenttomainas 2.6.34. RuneLite stays on 1.13.2, which is still the latest stable tag.Most of this release is crash fixes found in the client error telemetry. Each one is a guard or a client-thread move, so a script now fails cleanly where it used to throw.
Bank, walker and objects
walkToBankreturns false when no bank location is known. This crash had about 700 reports.Rs2Bankscroll helpers anddepositLootingBagtolerate unloaded widgets.ARRIVEDwhen a pass starts on the final path endpoint.clickObjectreturns false when the player or object location is unknown.Rs2TileObjectModel.clickhandles an unresolved impostor. The scene object scan stays inside the worldview tile array.Player, NPC and camera
Rs2Camera.angleToTileand theRs2NpcModeldistance helpers are null-safe without a local player.Rs2NpcModelid and the NPC and player cache snapshots are read on the client thread.Inventory, GE, shop and combat
Rs2Inventory.invokeMenuno longer throws an NPE when no widget matches the item slot.buyItemwith no free GE slot andprocessOfferwith a closed offer container fail cleanly.Rs2Shop.itemBounds,setAttackStyle,Rs2CannonandgetSlayerTaskare guarded.Rs2Cannonresolves the owned cannon from theOWNEDMCANNONvarp.Quest Helper
Agent server, config and logging
{}. Malformed or wrong-typed bodies get a 400 instead of a 500.OverlappingFileLockException.BlockingEventfailures log their stack trace.Release tree
main(2.6.33) back intodevelopmentfirst, so this branch carries the RuneLite 1.13.2 merge and849b66c0.d727b5932amergesorigin/developmentintomainwith no conflicts.0510c9f8f8bumpsmicrobot.versionfrom 2.6.33 to 2.6.34.developmentapart from that line.Test plan
:client:runUnitTestson the tree that equalsdevelopment: 2505 tests, 0 failures, 2 skippedmicrobot.version=2.6.34-PmicrobotClientPath), and the Hub tests pass: 926 tests, 0 failures, 2 skipped🤖 Generated with Claude Code
https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV