apache/dubbo · error · NullPointerException
threadFactory
Error message
threadFactory
What it means
NullPointerException thrown by the HashedWheelTimer constructor when the threadFactory parameter is null. HashedWheelTimer spawns a dedicated worker thread for ticking, so it requires a ThreadFactory to create that thread. This is a fail-fast validation at construction time — the parameter is mandatory and cannot be defaulted. Dubbo's HashedWheelTimer is a Netty-derived timing-wheel implementation used for timeout scheduling.
Source
Thrown at dubbo-common/src/main/java/org/apache/dubbo/common/timer/HashedWheelTimer.java:232
* {@link TimerTask} execution.
* @param tickDuration the duration between tick
* @param unit the time unit of the {@code tickDuration}
* @param ticksPerWheel the size of the wheel
* @param maxPendingTimeouts The maximum number of pending timeouts after which call to
* {@code newTimeout} will result in
* {@link java.util.concurrent.RejectedExecutionException}
* being thrown. No maximum pending timeouts limit is assumed if
* this value is 0 or negative.
* @throws NullPointerException if either of {@code threadFactory} and {@code unit} is {@code null}
* @throws IllegalArgumentException if either of {@code tickDuration} and {@code ticksPerWheel} is <= 0
*/
public HashedWheelTimer(
ThreadFactory threadFactory,
long tickDuration, TimeUnit unit, int ticksPerWheel,
long maxPendingTimeouts) {
if (threadFactory == null) {
throw new NullPointerException("threadFactory");
}
if (unit == null) {
throw new NullPointerException("unit");
}
if (tickDuration <= 0) {
throw new IllegalArgumentException("tickDuration must be greater than 0: " + tickDuration);
}
if (ticksPerWheel <= 0) {
throw new IllegalArgumentException("ticksPerWheel must be greater than 0: " + ticksPerWheel);
}
// Normalize ticksPerWheel to power of two and initialize the wheel.
wheel = createWheel(ticksPerWheel);
mask = wheel.length - 1;
// Convert tickDuration to nanos.
this.tickDuration = unit.toNanos(tickDuration);
View on GitHub (pinned to 3a3043227f)
Solutions
- Pass a non-null ThreadFactory — use Executors.defaultThreadFactory() if you have no custom requirement.
- If using dependency injection, verify the ThreadFactory bean is properly configured and not null.
- Add a null check before construction and supply a default factory.
Example fix
// before ThreadFactory factory = possiblyNullFactory; new HashedWheelTimer(factory, 100, TimeUnit.MILLISECONDS, 512); // after ThreadFactory factory = possiblyNullFactory != null ? possiblyNullFactory : Executors.defaultThreadFactory(); new HashedWheelTimer(factory, 100, TimeUnit.MILLISECONDS, 512);
Defensive patterns
Strategy: validation
Validate before calling
Objects.requireNonNull(threadFactory, "threadFactory must not be null"); // or supply a default: ThreadFactory factory = threadFactory != null ? threadFactory : Executors.defaultThreadFactory(); new HashedWheelTimer(factory, tickDuration, unit, ticksPerWheel);
Prevention
- Always pass Executors.defaultThreadFactory() or a custom non-null factory.
- Use Objects.requireNonNull() to fail with a clear message before reaching the constructor.
- If injecting via DI, mark the dependency @Required or validate at bean post-construction.
When it happens
Trigger: Constructing a HashedWheelTimer with an explicit null threadFactory argument. Typically a programming error where the caller failed to supply or resolve the ThreadFactory before passing it.
Common situations: Custom code constructing HashedWheelTimer directly with a ThreadFactory variable that was never assigned; dependency injection misconfiguration that injects null for the ThreadFactory bean; conditional logic that yields null in an edge case.
Related errors
- unit
- tickDuration must be greater than 0: {}
- ticksPerWheel must be greater than 0: {}
- Extension type == null
- Extension name == null
AI-assisted analysis of apache/dubbo@3a3043227f (2026-08-14).
Data as JSON: /api/errors/ef3daf24dd04eb22.
Report an issue: GitHub.