hiero-ledger / hiero-ledger/hiero-consensus-node

State singletons with `null` values

Open
#22,219 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
Java
Stars
406
Forks
226
Avg merge
3d 4h
Merged PRs (30d)
210

Description

Hi team,

With the `hedera-app` bump from 0.64.3 to 0.65.0 we noticed the following exception that started to appear in our logs:
```
2025-09-24 03:31:37.038
2025-09-24T00:31:37.028Z ERROR tomcat-handler-26249 c.h.n.a.w.h.DispatchProcessor Possibly CATASTROPHIC failure - exception thrown while handling dispatch java.lang.NullPointerException: null
at java.base/java.util.Objects.requireNonNull(Unknown Source)
at com.hedera.node.app.throttle.ThrottleServiceManager.resetThrottlesUnconditionally(ThrottleServiceManager.java:237)
at com.hedera.node.app.workflows.handle.throttle.DispatchUsageManager.screenForCapacity(DispatchUsageManager.java:72)
at com.hedera.node.app.workflows.handle.DispatchProcessor.tryHandle(DispatchProcessor.java:139)
at com.hedera.node.app.workflows.handle.DispatchProcessor.processDispatch(DispatchProcessor.java:114)
at com.hedera.node.app.workflows.standalone.TransactionExecutors.lambda$newExecutor$0(TransactionExecutors.java:224)
at com.hedera.node.app.workflows.standalone.TransactionExecutors$DefaultTracerBinding.runWhere(TransactionExecutors.java:326)
at com.hedera.node.app.workflows.standalone.TransactionExecutors.lambda$newExecutor$1(TransactionExecutors.java:223)
at org.hiero.mirror.web3.service.TransactionExecutionService.execute(TransactionExecutionService.java:83)
at org.hiero.mirror.web3.service.ContractCallService.doProcessCall(ContractCallService.java:126)
...
```

Please note that we have custom implementations of all needed in the state singletons, such as `EntityCountsSingleton`, `EntityIdSingleton`, `ThrottleUsageSingleton`, etc. All of them have a no-op `set(...)` method and a `get()` method that always returns a default instance. This is sufficient for our needs.

However, after the `hedera-app` bump to 0.65.0 we started noticing that the singletons in some rare cases started to have `null` values which results in exceptions such as the one above.

After comparing the changes in the `hedera-app` bump I noticed that `WritableSingletonStateBase#get()` was:
```
public T get() {
// If there is a modification, then we've already done a "put" or "remove"
// and should return based on the modification
if (isModified()) {
final var currentValue = currentValue();
return currentValue != null ? currentValue : super.get();
} else {
return super.get();
}
}
```

and became:
```
public T get() {
// If there is a modification, then we've already done a "put" or "remove"
// and should return based on the modification
if (isModified()) {
return currentValue();
} else {
return super.get();
}
}
```

Even with our custom implementations there are still cases like `WrappedWritableSingletonState.get()` that call this method directly bypassing our code. This change in the `WritableSingletonStateBase#get()` method now seems to allow `null` values which causes issues on our side.

Please let me know if my conclusions are correct and if so - is there some fix that can be done on your side? In which cases `null` is a valid value?

Contributor guide

Open the contributing guide

Research direction

Start with WritableSingletonStateBase#get() and WrappedWritableSingletonState.get(), then trace the call to ThrottleServiceManager.resetThrottlesUnconditionally shown in the stack trace. Compare the 0.64.3 and 0.65.0 behavior to determine when a null value is valid for modified singleton state. Done means the expected null semantics are established and the reported unexpected NullPointerException is prevented or clearly addressed.

Written by the indexing model from the issue text.

Assessment

Tech stack
java
Domain
backend
Issue type
Bug
Difficulty
4/5
Estimated time
3-5 days
Activity status
Stale
Clarity
Mostly clear
Newbie friendliness
45/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.