Analysis
When multiple Homebridge child bridges share the same hostname (e.g., pi4b.local), bonjour-service fails to populate device.addresses for some of them. This is due to mDNS response packet splitting - the A/AAAA records for the shared hostname are included in earlier response packets but suppressed in later ones per mDNS duplicate record guidelines. bonjour-service only looks for A/AAAA records within each individual packet (see buildServicesFor in browser.js), so services arriving in later packets get empty address arrays.
This causes startDiscovery() to skip the IP validation loop entirely for those instances, going straight from "Found" to "Could not register" with no "Testing" attempt.
Root cause
In bonjour-service/dist/lib/browser.js, buildServicesFor() (line 169-171):
records
.filter((rr) => (rr.type === 'A' || rr.type === 'AAAA') && dnsEqual(rr.name, service.host))
.forEach((rr) => service.addresses.push(rr.data));
records is scoped to a single mDNS response packet (packet.answers.concat(packet.additionals)). When multiple services share a hostname, the mDNS responder suppresses recently-sent A records in subsequent packets. Services in those later packets get addresses: [].
Expected Behavior
All Homebridge child bridges running on the same host are discovered and registered, regardless of how many share a hostname.
Steps To Reproduce
Set up Homebridge with 10+ child bridges on a single host
Use hap-client to discover instances
Observe discovery logs - some bridges show "Found" → "Testing" → "Success" → "Instance Registered", while others show "Found" → "Could not register" with no "Testing" line
The missing bridges have device.addresses: [] despite device.host containing a valid .local hostname
Logs
Configuration
13 child bridges, all on the same host
bonjour-service v1.3.0
avahi-daemon as mDNS responder
mDNS advertiser set to avahi in Homebridge config
Environment
- OS: Raspian Trixie
- Software: Homebridge 1.11.4
- Node: 24
- npm: 11
Process Supervisor
hb-service
Additional Context
The better fix is probably upstream in the bonjour-service, but this does seem to work.
Add a dns.lookup fallback in startDiscovery() when device.addresses is empty. dns.lookup uses the OS resolver (getaddrinfo), which respects /etc/nsswitch.conf and resolves .local hostnames via the system's mDNS stack (libnss-mdns/avahi).
if (device.addresses.length === 0 && device.host) {
try {
const dns = require('node:dns');
const { promisify } = require('node:util');
const lookup = promisify(dns.lookup);
const result = await lookup(device.host, { family: 4 });
device.addresses.push(result.address);
this.debug(`[HapClient] Discovery :: Resolved ${device.host} to ${result.address} for ${instance.username}`);
} catch (e) {
this.debug(`[HapClient] Discovery :: Failed to resolve ${device.host} for ${instance.username}: ${e.message}`);
}
}
This is inserted immediately before the for (const ip of device.addresses) loop.
Analysis
When multiple Homebridge child bridges share the same hostname (e.g.,
pi4b.local),bonjour-servicefails to populatedevice.addressesfor some of them. This is due to mDNS response packet splitting - the A/AAAA records for the shared hostname are included in earlier response packets but suppressed in later ones per mDNS duplicate record guidelines.bonjour-serviceonly looks for A/AAAA records within each individual packet (seebuildServicesForinbrowser.js), so services arriving in later packets get empty address arrays.This causes
startDiscovery()to skip the IP validation loop entirely for those instances, going straight from "Found" to "Could not register" with no "Testing" attempt.Root cause
In
bonjour-service/dist/lib/browser.js,buildServicesFor()(line 169-171):recordsis scoped to a single mDNS response packet (packet.answers.concat(packet.additionals)). When multiple services share a hostname, the mDNS responder suppresses recently-sent A records in subsequent packets. Services in those later packets getaddresses: [].Expected Behavior
All Homebridge child bridges running on the same host are discovered and registered, regardless of how many share a hostname.
Steps To Reproduce
Set up Homebridge with 10+ child bridges on a single host
Use hap-client to discover instances
Observe discovery logs - some bridges show "Found" → "Testing" → "Success" → "Instance Registered", while others show "Found" → "Could not register" with no "Testing" line
The missing bridges have device.addresses: [] despite device.host containing a valid .local hostname
Logs
Configuration
Environment
Process Supervisor
hb-service
Additional Context
The better fix is probably upstream in the bonjour-service, but this does seem to work.
Add a
dns.lookupfallback instartDiscovery()whendevice.addressesis empty.dns.lookupuses the OS resolver (getaddrinfo), which respects/etc/nsswitch.confand resolves.localhostnames via the system's mDNS stack (libnss-mdns/avahi).This is inserted immediately before the
for (const ip of device.addresses)loop.