#4474·keploy

Replay reports a mock mismatch for DNS names capture deliberately never records (NXDOMAIN search-expansion)

Author: Aditya-eddyCreated Aug 22, 2026Updated Sep 1, 2026

Parent: #2426

Summary

Capture deliberately drops non-Success DNS responses. Replay then counts those same queries as mock mismatches and fails the test. The two halves disagree, and with ndots:5 the cost is large: every hostname emits 2–3 search-expansion misses, so one app doing one lookup can fail an entire test set on names that never resolved in production either.

Evidence

Locally, after fixing the cross-namespace DNS search domain (#2428), a clean run still failed 8/8 with 16 DNS mismatches — and every single one was a search-expanded name:

6 × A     mongo.prod.svc.cluster.local.prod.svc.cluster.local.
6 × AAAA  mongo.prod.svc.cluster.local.prod.svc.cluster.local.
2 × A     openbao-prod-active.prod.svc.cluster.local.prod.svc.cluster.local.
2 × AAAA  openbao-prod-active.prod.svc.cluster.local.prod.svc.cluster.local.

Those doubled names are what the resolver probes first under ndots:5; they NXDOMAIN, and then the absolute name succeeds. Recorded DNS mocks: 4 — exactly the successful absolute A/AAAA lookups for those two hosts.

The capture side is explicit that this is intentional (enterprise/pkg/agent/proxy/dns_capture.go):

go
// Mirror keploy's existing DNS server: skip non-Success rcodes (NXDOMAIN,
// SERVFAIL, REFUSED). These are usually search-domain expansion noise
// and would pollute mocks.yaml without adding replay value.
if msg.Rcode != dns.RcodeSuccess { return }

pinned by TestDNSCapture_SkipsNXDomainResponses. That reasoning is sound — recording them would multiply DNS mocks several-fold.

But the replay side (keploy/keploy pkg/agent/proxy/dns.go, resolveUncachedDNSResponse) treats their absence as a defect: on a mock miss it forwards upstream, gets a negative answer, falls through to the synthetic response and then reports ErrMockNotFound + a MockMismatchReport.

Note the inconsistency already present in that function: NODATA (NOERROR + 0 answers) relays and returns early, producing no mismatch. Only NXDOMAIN falls through to the report — even though capture skips both equally.

What NOT to do

Synthesising NXDOMAIN back to the app would regress #2006 — the code says so:

go
// When mocking is enabled, a negative answer must NOT reach the app: any
// unknown name should resolve to the proxy IP so eBPF can intercept it and
// match against recorded mocks (relaying NXDOMAIN crashes apps using bare
// service names like "localstack" or "postgres" — issue #2006).

The #2006 steering must stay.

Fix

Keep the steering; stop reporting an expected negative. Only when upstream returned a definitive negative answer — if upstream was unreachable we cannot tell, so keep reporting there. This makes NXDOMAIN consistent with the NODATA branch above it.

Validated locally: DNS mismatches 16 → 0, and combined with the other fixes the run went to 8/8 passing.