NullPointerException in LinkedList.removeAdd / PhreakAccumulateNode during fireAllRules - Seeking Guidance [Drools v9.44.0.Final]

114 views
Skip to first unread message

Nont Banditwong

unread,
Mar 26, 2025, 5:52:57 AMMar 26
to Drools Usage
Hello,

I'm encountering a persistent NullPointerException deep within the Drools engine during rule execution, and I was hoping for some guidance or insights based on the stack trace signature, as I'm unfortunately unable to share the specific DRL or Java source code due to project confidentiality at this time. I understand this makes diagnosing difficult, but I appreciate any general pointers or suggestions you might have.

Problem:

The application executes fireAllRules(), and during this process, it consistently throws the following NullPointerException:

java.lang.NullPointerException: Cannot invoke "org.drools.core.util.DoubleLinkedEntry.setPrevious(org.drools.core.util.DoubleLinkedEntry)" because "next" is null
        at org.drools.core.util.LinkedList.removeAdd(LinkedList.java:275)
        at org.drools.core.util.index.TupleList.removeAdd(TupleList.java:30)
        at org.drools.core.phreak.RuleNetworkEvaluator.doUpdatesReorderRightMemory(RuleNetworkEvaluator.java:869)
        at org.drools.core.phreak.PhreakAccumulateNode.doNode(PhreakAccumulateNode.java:77)
        at org.drools.core.phreak.RuleNetworkEvaluator.switchOnDoBetaNode(RuleNetworkEvaluator.java:599)
        at org.drools.core.phreak.RuleNetworkEvaluator.evalBetaNode(RuleNetworkEvaluator.java:566)
        at org.drools.core.phreak.RuleNetworkEvaluator.evalNode(RuleNetworkEvaluator.java:393)
        at org.drools.core.phreak.RuleNetworkEvaluator.innerEval(RuleNetworkEvaluator.java:353)
        at org.drools.core.phreak.RuleNetworkEvaluator.evalStackEntry(RuleNetworkEvaluator.java:251)
        at org.drools.core.phreak.RuleNetworkEvaluator.outerEval(RuleNetworkEvaluator.java:194)
        at org.drools.core.phreak.RuleNetworkEvaluator.evaluateNetwork(RuleNetworkEvaluator.java:147)
        at org.drools.core.phreak.RuleExecutor.evaluateNetwork(RuleExecutor.java:230)
        at org.drools.kiesession.agenda.DefaultAgenda.evaluateEagerList(DefaultAgenda.java:503)
        at org.drools.core.phreak.RuleExecutor.ruleWithHigherSalienceActivated(RuleExecutor.java:276)
        at org.drools.core.phreak.RuleExecutor.fire(RuleExecutor.java:174)
        at org.drools.core.phreak.RuleExecutor.evaluateNetworkAndFire(RuleExecutor.java:92)
        at org.drools.core.concurrent.AbstractGroupEvaluator.evaluateAndFire(AbstractGroupEvaluator.java:48)
        at org.drools.kiesession.agenda.DefaultAgenda.fireLoop(DefaultAgenda.java:620)
        at org.drools.kiesession.agenda.DefaultAgenda.internalFireAllRules(DefaultAgenda.java:573)
        at org.drools.kiesession.agenda.DefaultAgenda.fireAllRules(DefaultAgenda.java:565)
        at org.drools.kiesession.session.StatefulKnowledgeSessionImpl.internalFireAllRules(StatefulKnowledgeSessionImpl.java:1093)
        at org.drools.kiesession.session.StatefulKnowledgeSessionImpl.fireAllRules(StatefulKnowledgeSessionImpl.java:1084)
        at org.drools.kiesession.session.StatefulKnowledgeSessionImpl.fireAllRules(StatefulKnowledgeSessionImpl.java:1068)


Context:

  • Drools Version: 9.44.0.Final
  • When it Occurs: The error seems to happen after we programmatically switch focus to a specific agenda group (agenda.getAgendaGroup("...").setFocus()) and then call kSession.fireAllRules().
  • Suspected Trigger: Based on the stack trace involving PhreakAccumulateNode and doUpdatesReorderRightMemory, it strongly suggests the issue occurs when the engine is processing updates related to an accumulate condition. This likely happens because a rule firing within the newly focused agenda group modifies (update) or retracts (delete) a fact that is involved in an accumulate statement (possibly in the same rule or another activated rule).
  • Debugging Done: We have enabled DEBUG logging for org.drools.core.reteoo and org.drools.core.phreak, reviewed the rules in the relevant agenda group (especially those using accumulate and performing modifications), but haven't pinpointed the exact cause yet.

Questions:

  1. Is this specific NullPointerException signature within LinkedList.removeAdd / TupleList.removeAdd during PhreakAccumulateNode processing a known issue in certain Drools versions or scenarios?
  2. Are there common patterns or types of fact modifications interacting with accumulate (e.g., modifying the accumulated object itself, modifying properties used in the accumulate constraints, retracting facts mid-accumulate) that are known to potentially cause such internal state inconsistencies?
  3. Beyond general DEBUG logging, are there any specific, more granular loggers, diagnostic flags, or alternative debugging techniques (that don't require sharing source) you would recommend focusing on to trace how the internal tuple lists related to the accumulate node might be getting corrupted?

Any pointers towards potential causes or further debugging strategies would be greatly appreciated!

Thanks for your time and expertise.

Nont

Gabriele Cardosi

unread,
Mar 26, 2025, 6:19:44 AMMar 26
to drools...@googlegroups.com, Mario Fusco
Hi,

looking at LinkedList#268:

T previous = node.getPrevious();
T next = node.getNext();
if (previous == null) {
next.setPrevious( null );
this.firstNode = next;
} else {
previous.setNext( next );
next.setPrevious( previous );
}
it seems there is a missing null-check on next.


@Mario Fusco ?


--
You received this message because you are subscribed to the Google Groups "Drools Usage" group.
To unsubscribe from this group and stop receiving emails from it, send an email to drools-usage...@googlegroups.com.
To view this discussion visit https://groups.google.com/d/msgid/drools-usage/f8c98a53-b988-47a3-8ce6-14e21e2e5a2en%40googlegroups.com.

Mario Fusco

unread,
Mar 26, 2025, 6:43:06 AMMar 26
to Gabriele Cardosi, drools...@googlegroups.com
Hi,

Gabriele, there isn't any missing null check there. The next should never be null, and if it is it means that there is some other problem elsewhere. Adding that null check will only (temporarily) fix the effect but not the root cause, making the original problem harder to investigate and fix.

Nont, we did a few fixes and refactors on accumulate before releasing the 10.0.0. Could you please update your project to that version and check if the problem persists? If so it would be great if you could send us a reproducer.

Thanks,
Mario

Gabriele Cardosi

unread,
Mar 26, 2025, 6:48:42 AMMar 26
to Mario Fusco, drools...@googlegroups.com
Hi Mario,
thanks for your explanation.
But then, there is still a kind of missing check, i.e. having next == null is an IllegalState (sort-of), so the code should intercept that situation and deal with it properly.
Instead, it currently relies on some "external" condition that, as this thread shows, may not be fullfilled.

WDYT ? 

Mario Fusco

unread,
Mar 26, 2025, 6:52:56 AMMar 26
to Gabriele Cardosi, drools...@googlegroups.com
But then, there is still a kind of missing check, i.e. having next == null is an IllegalState (sort-of), so the code should intercept that situation and deal with it properly.
Instead, it currently relies on some "external" condition that, as this thread shows, may not be fullfilled.

We could do this, but this will require adding some extra code just to basically throw a NullPointerException instead of a IllegalState one, without any clue of the root cause anyway. I don't see how the second would be more informative than the first.

Mario

Nont Banditwong

unread,
Mar 26, 2025, 8:05:27 AMMar 26
to Drools Usage

Hi Mario, Gabriele,

Thank you both very much for the quick replies and the insightful discussion regarding the potential null check and the focus on the root cause.

Mario, following your suggestion, I have upgraded our project dependencies and tested specifically with Drools version 10.0.0

Unfortunately, the result is the same – we are still encountering the exact same NullPointerException with the identical stack trace originating from LinkedList.removeAdd / TupleList.removeAdd during PhreakAccumulateNode processing when fireAllRules() is called after switching agenda group focus.

Perhaps looking at a common pattern might help? Several rules in the agenda group where the error occurs use an accumulate pattern similar to this in their LHS:

$item_filter0 : List(size() >= N) from accumulate ( // N is typically 1 or 2
        $filter: Item(productCode in ("someCode", "anotherCode")) from $cart.getExpandedItems(),
        collectList($filter)
)

Thank you again for your time and expertise. Any further thoughts, however general, are appreciated.

Best regards,

Nont

Mario Fusco

unread,
Mar 26, 2025, 8:54:00 AMMar 26
to drools...@googlegroups.com
Hi Nont,

I'm afraid that the information that you provided is far from being enough to create a reproducer of the problem and investigate it. Can't you share a project (minimized and anonymized if necessary) that reproduces the NPE so we could try to understand what's happening?

Thanks,
Mario

Nont Banditwong

unread,
Mar 28, 2025, 11:10:07 PMMar 28
to Drools Usage

Hi Mario,

Following up on our discussion, I've created a minimal sample project that reproduces the NullPointerException. You can find it here: https://github.com/nontster/drools-sample.   

Hopefully, this will give you the necessary context to investigate the issue further. Please let me know if you have any questions or need additional information.

Thanks, Nont

Toshiya Kobayashi

unread,
Apr 1, 2025, 3:49:58 AMApr 1
to Drools Usage
Hi Nont,

Thank you for sharing the reproducer. I confirmed the issue is reproducible with the current main branch.

Filed a github issue for this: https://github.com/apache/incubator-kie-drools/issues/6297

I will investigate further and work on a fix with Mario.

We will keep you posted.

Regards,
Toshiya

2025年3月29日土曜日 12:10:07 UTC+9 nont...@gmail.com:

Toshiya Kobayashi

unread,
Apr 16, 2025, 1:48:47 AMApr 16
to Drools Usage
Hi Nont,

The issue has been fixed. The fix will be available in version 10.2.0 (no release date forecast yet).

Regarding workarounds for now,

As you have clarified in your reproducer, the conditions to reproduce are

- Share a subnetwork (= the first argument in "from accumulate" e.g. in the reproducer, $filter:Item() from $cart.getItemList()) among more than 3 rules
- More than 2 rules have the same agenda-group, the others (one or more rules) have a different agenda-group

So,

Workaround A)

- Use the same agenda-group among such rules

but it may be difficult to change it depending on your rules.


Workaround B)

- Avoid subnetwork sharing by slightly modifying the subnetwork pattern

For example, if you add `and eval (true)` to "no2" or "no3" rule,

```
rule "no2"
...
when
    $cart :Cart( itemList.size()>0)
    $item_filter0 :List() from accumulate (
        $filter:Item() from $cart.getItemList() and eval(true),
        collectList($filter)
  )
```
you can work around the issue. However, adding it to "no4" doesn't solve the issue, because it still keep grouping <"no2" and "no3"> vs <"no4">. If you want to avoid such trial-and-error, you may add unique eval to all those rules. for example,

```
rule "no2"
...
when
    $cart :Cart( itemList.size()>0)
    $item_filter0 :List() from accumulate (
        $filter:Item() from $cart.getItemList() and eval(1==1),
        collectList($filter)
  )
...

rule "no3"
...
when
    $cart :Cart( itemList.size()>0)
    $spend_filter0 :Number(intValue >= 13000) from accumulate (
        $filter:Item() from $cart.getItemList() and eval(2==2),
        sum($filter.getPrice())
  )
...

rule "no4"
...
when
    $cart :Cart( itemList.size()>0)
    $spend_filter0 :Number(intValue >= 11000) from accumulate (
        $filter:Item() from $cart.getItemList() and eval(3==3),
        sum($filter.getPrice())
  )
...
```

This workaround may have a slight performance impact, but I hope it's negligible.

Regards,
Toshiya


2025年4月1日火曜日 16:49:58 UTC+9 Toshiya Kobayashi:
Reply all
Reply to author
Forward
Message has been deleted
Message has been deleted
0 new messages