Skip to content

Fix startup NPE when a weakly dependent registry is unavailable (check=false)#16356

Open
AryamannSingh7 wants to merge 1 commit into
apache:3.3from
AryamannSingh7:fix/16178-null-registry-startup
Open

Fix startup NPE when a weakly dependent registry is unavailable (check=false)#16356
AryamannSingh7 wants to merge 1 commit into
apache:3.3from
AryamannSingh7:fix/16178-null-registry-startup

Conversation

@AryamannSingh7

Copy link
Copy Markdown

What is the purpose of the change?

Fixes #16178.

When a service subscribes to multiple registries and one of them is weakly dependent (check="false") and unavailable at startup, the application fails to start with:

java.lang.NullPointerException: Cannot invoke "org.apache.dubbo.registry.Registry.getUrl()" because "this.registry" is null
    at org.apache.dubbo.registry.ListenerRegistryWrapper.getUrl(ListenerRegistryWrapper.java:44)
    at org.apache.dubbo.registry.integration.RegistryDirectory.subscribe(RegistryDirectory.java:...)

This is a regression: Dubbo 3.2.03.2.4 start successfully, 3.2.5+ fail (also reproduced on 3.3.x).

Root cause

Under check=false, when a registry cannot be created (the registry is down), AbstractRegistryFactory#getRegistry swallows the exception and returns null. RegistryFactoryWrapper still wraps this null into a ListenerRegistryWrapper, so a null delegate is an expected stateListenerRegistryWrapper already guards register / unregister / subscribe with if (registry != null).

However:

  • getUrl / isAvailable / destroy / unsubscribe / isServiceDiscovery / lookup were not guarded.
  • RegistryDirectory#subscribe computes a metrics cluster name via registry.getUrl().getParameter(...) unconditionally. That line was introduced in Support multi registries metrics key #12582 (released in 3.2.5), which is exactly why the NPE appears from 3.2.5 onward.

What changed

  1. ListenerRegistryWrapper — complete the existing null-safety: getUrl returns null, isAvailable / isServiceDiscovery return false, destroy / unsubscribe become no-ops, and lookup returns an empty list when the delegate registry is null.
  2. RegistryDirectory#subscribe — guard the metrics cluster-name computation against a null registry URL (no cluster name is reported when the registry is unavailable). RegistryEvent#toSubscribeEvent already tolerates a null name.

Verification

Added ListenerRegistryWrapperTest#testNullRegistryIsTolerated, which reproduces the exact NPE without the fix and passes with it. :dubbo-registry-api tests and spotless:check pass locally (JDK 21).

Checklist

  • Make sure there is a GitHub issue field for the change.
  • Write a pull request description that is detailed enough to understand what the pull request does, how, and why.
  • Write necessary unit-test to verify your logic correction.
  • Make sure GitHub actions can pass.

@codecov-commenter

codecov-commenter commented Jun 25, 2026

Copy link
Copy Markdown

Codecov Report

❌ Patch coverage is 61.53846% with 5 lines in your changes missing coverage. Please review.
✅ Project coverage is 60.87%. Comparing base (c587ff6) to head (2b13bb7).

Files with missing lines Patch % Lines
...apache/dubbo/registry/ListenerRegistryWrapper.java 62.50% 1 Missing and 2 partials ⚠️
.../dubbo/registry/integration/RegistryDirectory.java 60.00% 1 Missing and 1 partial ⚠️
Additional details and impacted files
@@             Coverage Diff              @@
##                3.3   #16356      +/-   ##
============================================
+ Coverage     60.51%   60.87%   +0.36%     
- Complexity    10252    11757    +1505     
============================================
  Files          1953     1953              
  Lines         89261    89265       +4     
  Branches      13471    13472       +1     
============================================
+ Hits          54015    54343     +328     
+ Misses        29611    29326     -285     
+ Partials       5635     5596      -39     
Flag Coverage Δ
integration-tests-java21 32.07% <30.76%> (?)
integration-tests-java8 32.29% <30.76%> (?)
samples-tests-java21 32.14% <30.76%> (-0.06%) ⬇️
samples-tests-java8 29.81% <30.76%> (+<0.01%) ⬆️
unit-tests-java11 59.10% <61.53%> (-0.03%) ⬇️
unit-tests-java17 58.61% <53.84%> (+0.03%) ⬆️
unit-tests-java21 58.58% <61.53%> (-0.03%) ⬇️
unit-tests-java25 58.57% <61.53%> (+<0.01%) ⬆️
unit-tests-java8 59.13% <66.66%> (-0.04%) ⬇️

Flags with carried forward coverage won't be shown. Click here to find out more.

☔ View full report in Codecov by Harness.
📢 Have feedback on the report? Share it here.

🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.
  • 📦 JS Bundle Analysis: Save yourself from yourself by tracking and limiting bundle sizes in JS merges.

@LI123456mo LI123456mo left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Reviewed the changes. The fix correctly guards all unprotected methods in ListenerRegistryWrapper (getUrl, isAvailable, destroy, unsubscribe, isServiceDiscovery, lookup) against a null delegate, and the RegistryDirectory#subscribe change safely extracts registry.getUrl() into a local variable before use. The test reproduces the exact NPE scenario.
@zrlw check it aslo

LGTM.

…check=false

When a registry cannot be created and check=false (e.g. a weakly dependent
ZooKeeper is down at startup), AbstractRegistryFactory returns a null registry
which RegistryFactoryWrapper still wraps in a ListenerRegistryWrapper. The
wrapper already guards register/unregister/subscribe against a null delegate,
but getUrl/isAvailable/destroy/unsubscribe/isServiceDiscovery/lookup did not,
and RegistryDirectory#subscribe dereferences registry.getUrl() unconditionally
to build a metrics label (added in apache#12582), causing an NPE since 3.2.5.

Complete the null-safety in ListenerRegistryWrapper and guard the metrics
label computation in RegistryDirectory#subscribe so a service can still start
when a non-critical registry is unavailable.

Fixes apache#16178
@AryamannSingh7 AryamannSingh7 force-pushed the fix/16178-null-registry-startup branch from 15d657b to 2b13bb7 Compare July 9, 2026 18:47
@AryamannSingh7

Copy link
Copy Markdown
Author

Hi @zrlw — following up here since you were tagged on this one.

Quick summary of why this matters: under a multi-registry setup where one registry is weakly dependent (check=false) and unavailable at startup, the application fails to boot with an NPE rather than degrading gracefully — which is the entire point of check=false. It's a regression: 3.2.03.2.4 start fine, 3.2.5+ do not, and it reproduces on 3.3.x.

The cause is that AbstractRegistryFactory#getRegistry returns null under check=false when the registry can't be created, and RegistryFactoryWrapper wraps that null into a ListenerRegistryWrapper. A null delegate is therefore an expected state — register / unregister / subscribe already guard for it — but getUrl / isAvailable / destroy / unsubscribe / isServiceDiscovery / lookup were never guarded. Separately, RegistryDirectory#subscribe calls registry.getUrl().getParameter(...) unconditionally for the metrics cluster name; that line came in with #12582, released in 3.2.5, which is exactly where the regression starts.

The change just completes the null-safety that was already half-present, and guards the metrics lookup. There's no behaviour change when the registry is non-null. ListenerRegistryWrapperTest#testNullRegistryIsTolerated reproduces the NPE without the fix and passes with it; :dubbo-registry-api tests and spotless:check are green locally on JDK 21.

The branch is now rebased onto current 3.3, so CI should be running clean. Happy to adjust the approach if you'd prefer the guard live somewhere else (e.g. in RegistryFactoryWrapper, by not wrapping a null registry at all). Would appreciate a look when you have time. Thanks!

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.

[Bug] Service startup fails when a weakly dependent ZooKeeper is down

3 participants