Skip to content

fix(aiofighter): guard null cache inventory setup after equipping slayer gear (2.2.9) - #638

Merged
chsami merged 1 commit into
developmentfrom
claude/err-aiofighter-cache-setup-null-npe
Oct 15, 2026
Merged

chsami merged 1 commit into
developmentfrom
claude/err-aiofighter-cache-setup-null-npe

Conversation

@chsami

@chsami chsami commented Oct 15, 2026

Copy link
Copy Markdown
Owner

Problem

Client error telemetry group 2180: 1 event from 1 reporter (client 2.6.32, AIOFighter 2.2.3), thread BankerScript-1:

java.lang.NullPointerException: Cannot invoke "net.runelite.client.plugins.microbot.inventorysetups.Inventory...
  at AIOFighterPlugin.setCurrentSlayerInventorySetup:254
  at BankerScript.withdrawUpkeepItems:476
  at BankerScript.handleBanking:496
  at BankerScript.lambda$run$2:64

Root cause

When a slayer task needs protective gear, BankerScript.withdrawUpkeepItems withdraws and equips it, then calls inventorySetupsPlugin.addInventorySetup("cache"). If the "cache" setup does not exist yet, the client's MInventorySetupsPlugin.addInventorySetup(String) only queues its creation with clientThread.invokeLater. The lookup on the next line (findFirst().orElse(null)) then finds nothing, and the null goes to AIOFighterPlugin.setCurrentSlayerInventorySetup, which calls getName() without a null check.

Change

  • AIOFighterPlugin.setCurrentSlayerInventorySetup now ignores a null setup: it logs Inventory setup not found; keeping current inventory setup and returns without writing config. This covers every caller, including BankerScript and InventorySetupUtil. Before, a null setup always threw, so no working behaviour is lost.
  • BankerScript has a new null-safe helper, findSetupByName(Collection<InventorySetup>, String). withdrawUpkeepItems now runs sleepUntil(..., 3000) until the "cache" setup appears, then looks it up. This waits on a condition on the BankerScript thread, not on the client thread.
  • AIOFighter version goes from 2.2.8 to 2.2.9.

Validation

  • New test BankerScriptCacheSetupTest (3 tests). It covers the helper (case-insensitive match, null and empty inputs, "cache" not registered yet) and checks that the setter ignores null without touching the config manager. Without the fix, the setter test fails with the original NPE.
  • ./gradlew test -PpluginList=AIOFighterPlugin --tests 'net.runelite.client.plugins.microbot.aiofighter.*' --tests '...PluginManifestVersionTest': 34 tests, 0 failures.

Known gaps

  • The real fix for the async behaviour belongs in the client: addInventorySetup(String) could return the setup or a future. That is out of scope for this repo.
  • If the setup still has not appeared after 3 seconds, this banking pass keeps the current setup instead of switching to "cache".
  • The poll reads the client's static inventorySetups list while the client thread may be adding to it. The old one-shot lookup had the same race, and sleepUntil catches and logs exceptions.

🤖 Generated with Claude Code

https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV

…yer gear (2.2.9)

addInventorySetup("cache") registers the setup on the client thread, so the
immediate lookup could miss it and pass null to setCurrentSlayerInventorySetup,
which threw an NPE on getName(). Wait for the cache setup to register before
looking it up, and make the setter ignore a null setup.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01LjtEMzQHGuq4M1Mpqk9pPV
@chsami
chsami merged commit 6f54d34 into development Oct 15, 2026
1 check passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant