Bug NPE in Maximizer while executing an ASH script

Veracity

Developer
Staff member
This is new. From the gCLI:

Maximizer: advs
Maximizing...
Net income = 1,338,172 Meat in 389 turns. Meat/Adventure = 3,440
Cumulative income = 1,073,713,429 Meat in 592,024 turns. Meat/Adventure = 1,813
(VeracityMeatFarm.ash, line 8462)
Script execution aborted (java.lang.NullPointerException: Cannot invoke "java.lang.Comparable.compareTo(Object)" because "left" is null): (VeracityMeatFarm.ash, line 8462)

Line 8462 is, essentially, the last line of the script. It's in a try/finally. The actual exception was obviously when the Maximizer was trying to maximize for "advs"; it aborted before actually putting on any equipment.

Here is the stack trace:

Code:
(VeracityMeatFarm.ash, line 8462)
class java.lang.NullPointerException: Cannot invoke "java.lang.Comparable.compareTo(Object)" because "left" is null
java.lang.NullPointerException: Cannot invoke "java.lang.Comparable.compareTo(Object)" because "left" is null
    at net.java.dev.spellcast.utilities.SortedListModel.compare(SortedListModel.java:256)
    at net.java.dev.spellcast.utilities.SortedListModel.normalIndexOf(SortedListModel.java:169)
    at net.java.dev.spellcast.utilities.SortedListModel.indexOf(SortedListModel.java:127)
    at net.sourceforge.kolmafia.AdventureResult.getCount(AdventureResult.java:1083)
    at net.sourceforge.kolmafia.session.InventoryManager.getCount(InventoryManager.java:131)
    at net.sourceforge.kolmafia.session.InventoryManager.getCount(InventoryManager.java:127)
    at net.sourceforge.kolmafia.KoLCharacter.recalculateAdjustments(KoLCharacter.java:5039)
    at net.sourceforge.kolmafia.Speculation.calculate(Speculation.java:171)
    at net.sourceforge.kolmafia.maximizer.MaximizerSpeculation.getScore(MaximizerSpeculation.java:61)
    at net.sourceforge.kolmafia.maximizer.Evaluator.enumerateEquipment(Evaluator.java:1830)
    at net.sourceforge.kolmafia.maximizer.Maximizer.maximize(Maximizer.java:145)
    at net.sourceforge.kolmafia.maximizer.Maximizer.maximize(Maximizer.java:1604)
    at net.sourceforge.kolmafia.maximizer.Maximizer.maximize(Maximizer.java:90)
    at net.sourceforge.kolmafia.textui.RuntimeLibrary.maximize(RuntimeLibrary.java:7306)
    at net.sourceforge.kolmafia.textui.RuntimeLibrary.maximize(RuntimeLibrary.java:7287)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
    at java.base/jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:77)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at net.sourceforge.kolmafia.textui.parsetree.LibraryFunction.execute(LibraryFunction.java:63)
    at net.sourceforge.kolmafia.textui.parsetree.FunctionCall.execute(FunctionCall.java:113)
    at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:423)
    at net.sourceforge.kolmafia.textui.parsetree.UserDefinedFunction.execute(UserDefinedFunction.java:87)
    at net.sourceforge.kolmafia.textui.parsetree.FunctionCall.execute(FunctionCall.java:113)
    at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:423)
    at net.sourceforge.kolmafia.textui.parsetree.Try.execute(Try.java:36)
    at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:423)
    at net.sourceforge.kolmafia.textui.parsetree.UserDefinedFunction.execute(UserDefinedFunction.java:87)
    at net.sourceforge.kolmafia.textui.AshRuntime.executeScope(AshRuntime.java:254)
    at net.sourceforge.kolmafia.textui.AshRuntime.execute(AshRuntime.java:182)
    at net.sourceforge.kolmafia.textui.AshRuntime.execute(AshRuntime.java:175)
    at net.sourceforge.kolmafia.textui.command.CallScriptCommand.call(CallScriptCommand.java:201)
    at net.sourceforge.kolmafia.textui.command.CallScriptCommand.run(CallScriptCommand.java:34)
    at net.sourceforge.kolmafia.KoLmafiaCLI.doExecuteCommand(KoLmafiaCLI.java:453)
    at net.sourceforge.kolmafia.KoLmafiaCLI.executeCommand(KoLmafiaCLI.java:419)
    at net.sourceforge.kolmafia.KoLmafiaCLI.executeLine(KoLmafiaCLI.java:338)
    at net.sourceforge.kolmafia.KoLmafiaCLI.executeLine(KoLmafiaCLI.java:225)
    at net.sourceforge.kolmafia.swingui.button.LoadScriptButton$LoadScriptRunnable.run(LoadScriptButton.java:26)
    at net.sourceforge.kolmafia.RequestThread$SequencedRunnable.run(RequestThread.java:343)
    at java.base/java.util.concurrent.Executors$RunnableAdapter.call(Executors.java:539)
    at java.base/java.util.concurrent.FutureTask.run(FutureTask.java:264)
    at java.base/java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1136)
    at java.base/java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:635)
    at java.base/java.lang.Thread.run(Thread.java:833)
Yes. It's in the "maximize" function of the RuntimeLibrary, in the Maximizer, and in Speculation.calculate, in particular.

The script is robust, in that you can rerun it and it will end up taking up where it left off.

Maximizer: advs
Maximizing...
60 combinations checked, best score 62.00
Putting on leather aviator's cap...
Equipment changed.
Wielding Mer-kin hookspear...
Equipment changed.
Holding blue LavaCo Lamp™...
Equipment changed.
Putting on octolus-skin cloak...
Equipment changed.
Putting on General Sage's Lonely Diamonds Club Jacket...
Equipment changed.
Putting on Crimbylow-rise jeans...
Equipment changed.
Putting on fudgecycle...
Equipment changed.
Putting on Spacegate scientist's insignia...
Equipment changed.
Putting on gingerbeard...
Equipment changed.
Putting on solid shifting time weirdness...
Equipment changed.
Net income = 0 Meat in 0 turns. Meat/Adventure = 0
Cumulative income = 1,073,713,429 Meat in 592,024 turns. Meat/Adventure = 1,813[/code]
 
Looking at that more closely:

Code:
class java.lang.NullPointerException: Cannot invoke "java.lang.Comparable.compareTo(Object)" because "left" is null
java.lang.NullPointerException: Cannot invoke "java.lang.Comparable.compareTo(Object)" because "left" is null
    at net.java.dev.spellcast.utilities.SortedListModel.compare(SortedListModel.java:256)
    at net.java.dev.spellcast.utilities.SortedListModel.normalIndexOf(SortedListModel.java:169)
    at net.java.dev.spellcast.utilities.SortedListModel.indexOf(SortedListModel.java:127)
    at net.sourceforge.kolmafia.AdventureResult.getCount(AdventureResult.java:1083)
    at net.sourceforge.kolmafia.session.InventoryManager.getCount(InventoryManager.java:131)
That's here:

Code:
  public static final int getCount(final AdventureResult item) {
    return item.getCount(KoLConstants.inventory);
  }

KoLConstants.inventory is a SortedListModel. SortedListModel.normalIndexOf is a binary search. The NPE is being thrown from here:

Code:
            // calculate the halfway point and compare the element with the
            // element located at the halfway point - note that in locating
            // the last index of, the value is rounded up to avoid an infinite
            // recursive loop
    
            int halfwayIndex = beginIndex + endIndex >> 1;
            compareResult = this.compare( (Comparable) this.get( halfwayIndex ), element );

Which is to say, "this.get( halfwayIndex )" got a null.

How could a null end up in inventory?
 
Apparently, this happens sometimes. Also see:
 
Based on what I've seen in other places:
* KoLConstants.inventory is mutable, accessible from multiple threads, and a LockableListModel
* LockableListModel.get(int index) returns null if index is larger than the length of the list, or less than zero

I expect that (somewhere, potentially not here) a get call which is expected to return a real item winds up returning null (because it gets by index, and the list is mutated after the index is stored to contain fewer elements). After that, either the null gets added to the list ready to crash in later places, or it crashes immediately.

Suspect it's not happening here because to happen here the inventory would have to halve in size, which seems unlikely.
 
Suspect it's not happening here because to happen here the inventory would have to halve in size
Why? At the bottom of a binary search, you're dealing with ~2 elements. If you were searching for the lexicographically last element in your inventory, any reduction in size in a separate thread could cause this to happen.

We really shouldn't be doing any kind of search or iteration concurrently with any mutating operations (add/remove in particular).
 
I got the same error this week using TourGuide.ash.
The orange line below is the reported error. So not just the maximizer?
Rich (BB code):
 foreach key, cc in combinations
            {
                if (final_combinations.count() >= setting_maximum_display_limit + 1)
                    break;
                //So, using item_amount() is three times faster than available_amount(), even though it ignores a bunch of stuff. Which is a difference of 0.3 seconds vs 0.1 seconds.
                if (cc.candy_1.item_amount() + cc.candy_1.closet_amount() == 0)
                    continue;
                if (cc.candy_2.item_amount() + cc.candy_2.closet_amount() == 0)
                    continue;
                if (cc.candy_1 == cc.candy_2 && cc.candy_1.item_amount() + cc.candy_1.closet_amount() < 2)
                    continue;


Rich (BB code):
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
             KoLmafia r26376-M, Mac OS X, Java 17.0.2
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
 Please note: do not post this log in the KoLmafia thread of KoL's
 Gameplay-Discussion forum. If you would like the KoLmafia dev team
 to look at it, please write a bug report at kolmafia.us. Include
 specific information about what you were doing when you made this
 and include this log as an attachment.
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
 Timestamp: Mon May 02 22:25:01 CDT 2022
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=
 User: Darwinlet
 Current run: 646
 MRU Script: Autoscend.ash
=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=


(relay_TourGuide.ash, line 32900)
class java.lang.NullPointerException: Cannot invoke "java.lang.Comparable.compareTo(Object)" because "left" is null
java.lang.NullPointerException: Cannot invoke "java.lang.Comparable.compareTo(Object)" because "left" is null
    at net.java.dev.spellcast.utilities.SortedListModel.compare(SortedListModel.java:256)
    at net.java.dev.spellcast.utilities.SortedListModel.normalIndexOf(SortedListModel.java:169)
    at net.java.dev.spellcast.utilities.SortedListModel.indexOf(SortedListModel.java:127)
    at net.sourceforge.kolmafia.AdventureResult.getCount(AdventureResult.java:1111)
    at net.sourceforge.kolmafia.textui.RuntimeLibrary.item_amount(RuntimeLibrary.java:5267)
    at jdk.internal.reflect.GeneratedMethodAccessor107.invoke(Unknown Source)
    at java.base/jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
    at java.base/java.lang.reflect.Method.invoke(Method.java:568)
    at net.sourceforge.kolmafia.textui.parsetree.LibraryFunction.execute(LibraryFunction.java:63)
    at net.sourceforge.kolmafia.textui.parsetree.FunctionCall.execute(FunctionCall.java:113)
    at net.sourceforge.kolmafia.textui.parsetree.Operator.applyTo(Operator.java:574)
    at net.sourceforge.kolmafia.textui.parsetree.Operation.execute(Operation.java:72)
    at net.sourceforge.kolmafia.textui.parsetree.Operator.applyTo(Operator.java:574)
    at net.sourceforge.kolmafia.textui.parsetree.Operation.execute(Operation.java:72)
    at net.sourceforge.kolmafia.textui.parsetree.Conditional.execute(Conditional.java:41)
    at net.sourceforge.kolmafia.textui.parsetree.If.execute(If.java:35)
    at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:423)
    at net.sourceforge.kolmafia.textui.parsetree.Loop.execute(Loop.java:23)
    at net.sourceforge.kolmafia.textui.parsetree.ForEachLoop.executeSlice(ForEachLoop.java:131)
    at net.sourceforge.kolmafia.textui.parsetree.ForEachLoop.execute(ForEachLoop.java:67)
    at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:423)
    at net.sourceforge.kolmafia.textui.parsetree.Conditional.execute(Conditional.java:54)
    at net.sourceforge.kolmafia.textui.parsetree.If.execute(If.java:35)
    at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:423)
    at net.sourceforge.kolmafia.textui.parsetree.Loop.execute(Loop.java:23)
    at net.sourceforge.kolmafia.textui.parsetree.ForEachLoop.executeSlice(ForEachLoop.java:131)
    at net.sourceforge.kolmafia.textui.parsetree.ForEachLoop.execute(ForEachLoop.java:67)
    at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:423)
    at net.sourceforge.kolmafia.textui.parsetree.UserDefinedFunction.execute(UserDefinedFunction.java:87)
    at net.sourceforge.kolmafia.textui.parsetree.FunctionCall.execute(FunctionCall.java:113)
    at net.sourceforge.kolmafia.textui.parsetree.FunctionInvocation.execute(FunctionInvocation.java:87)
    at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:423)
    at net.sourceforge.kolmafia.textui.parsetree.Loop.execute(Loop.java:23)
    at net.sourceforge.kolmafia.textui.parsetree.ForEachLoop.executeSlice(ForEachLoop.java:131)
    at net.sourceforge.kolmafia.textui.parsetree.ForEachLoop.execute(ForEachLoop.java:67)
    at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:423)
    at net.sourceforge.kolmafia.textui.parsetree.Loop.execute(Loop.java:23)
    at net.sourceforge.kolmafia.textui.parsetree.ForEachLoop.executeSlice(ForEachLoop.java:135)
    at net.sourceforge.kolmafia.textui.parsetree.ForEachLoop.execute(ForEachLoop.java:67)
    at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:423)
    at net.sourceforge.kolmafia.textui.parsetree.Else.execute(Else.java:26)
    at net.sourceforge.kolmafia.textui.parsetree.If.execute(If.java:43)
    at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:423)
    at net.sourceforge.kolmafia.textui.parsetree.UserDefinedFunction.execute(UserDefinedFunction.java:87)
    at net.sourceforge.kolmafia.textui.parsetree.FunctionCall.execute(FunctionCall.java:113)
    at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:423)
    at net.sourceforge.kolmafia.textui.parsetree.UserDefinedFunction.execute(UserDefinedFunction.java:87)
    at net.sourceforge.kolmafia.textui.parsetree.FunctionCall.execute(FunctionCall.java:113)
    at net.sourceforge.kolmafia.textui.parsetree.BasicScope.execute(BasicScope.java:423)
    at net.sourceforge.kolmafia.textui.parsetree.UserDefinedFunction.execute(UserDefinedFunction.java:87)
    at net.sourceforge.kolmafia.textui.AshRuntime.executeScope(AshRuntime.java:254)
    at net.sourceforge.kolmafia.textui.AshRuntime.execute(AshRuntime.java:182)
    at net.sourceforge.kolmafia.KoLmafiaASH.getClientHTML(KoLmafiaASH.java:147)
    at net.sourceforge.kolmafia.KoLmafiaASH.getClientHTML(KoLmafiaASH.java:115)
    at net.sourceforge.kolmafia.KoLmafiaASH.getClientHTML(KoLmafiaASH.java:89)
    at net.sourceforge.kolmafia.request.RelayRequest.handleSimple(RelayRequest.java:3257)
    at net.sourceforge.kolmafia.request.RelayRequest.run(RelayRequest.java:3284)
    at net.sourceforge.kolmafia.RequestThread.postRequest(RequestThread.java:242)
    at net.sourceforge.kolmafia.RequestThread.postRequest(RequestThread.java:207)
    at net.sourceforge.kolmafia.webui.RelayAgent.readServerResponse(RelayAgent.java:453)
    at net.sourceforge.kolmafia.webui.RelayAgent.performRelay(RelayAgent.java:103)
    at net.sourceforge.kolmafia.webui.RelayAgent.run(RelayAgent.java:82)
(relay_TourGuide.ash, line 32900)
 
Back
Top