Known Limitations

What WhisperGraph doesn't know: subdomain WHOIS, anycast GeoIP, BGP route flux, threat-feed staleness, and Cypher write restrictions.

Updated May 2026

Known Limitations Documentation

What WhisperGraph doesn't know — published explicitly so you can plan around the gaps. Updated as the graph and ingestion pipelines evolve.


DNS

Subdomain WHOIS history — WHOIS history is captured per registrable domain. whisper.history("www.cloudflare.com") returns nothing because WHOIS doesn't apply to subdomains. Use the parent domain.

Recursive DNS observation gaps — Observations come from passive DNS, public resolvers, and our own probes. Domains that resolve only via private DNS (split-horizon, internal AD) are not observable. RESOLVES_TO will be empty for these.

Stale CNAME chains — CNAME chains are observed at resolution time. If a target domain is deleted, the chain may persist in the graph for up to 24 hours before being marked stale.


GeoIP

Anycast IPs — One IP, many physical locations. Cloudflare 1.1.1.1, Google DNS 8.8.8.8, etc. GeoIP for these returns the publisher's nominal HQ, not the actual edge a user hits. Don't use GeoIP for anycast IPs to draw geographic conclusions.

Mobile carrier NAT — Carrier-grade NAT pools span continents. GeoIP for a mobile IP is a guess.

VPN exit nodes — GeoIP returns the exit location, not the user's true location.


BGP

Route flux — A prefix may be announced and withdrawn many times per hour during instability. The graph stores the current announcement; historical hops require whisper.history().

MOAS conflicts — Real hijacks vs legitimate anycast both produce MOAS. WhisperGraph reports the conflict via the CONFLICTS_WITH edge; interpretation requires context (RPKI status, ASN reputation, history).

Inferred peeringPEERS_WITH edges are inferred from BGP path observations, not from confirmed peering agreements. They're directionally correct but the metadata (peering type, capacity) isn't ground truth.


Threat intelligence

False positives — Low-quality feeds occasionally list legitimate infrastructure. Filter by feed category or use explain() which weights feeds by reliability.

No malware sample data — File hashes appear in some feeds but the graph does not store the actual samples. Use external sandboxes.

Live backend dependencyexplain() and whisper.history() fetch from the whisper-feeds service. If it is briefly unavailable they return available: false with a retryAfter value rather than a score — respect the retry interval.


API and Cypher

Read-only — No CREATE, MERGE, SET, DELETE. See FAQ.

No procedures for write operations — All procedures (explain, whisper.variants, whisper.history, whisper.quota, and the db.* introspection calls) are read-only.

Plan-tier depth limits — Anonymous requests are capped at 2 hops; higher tiers go deeper. For plan limits see the pricing page.

Regex is supported but slow=~ regex matching works (full-match semantics, maximum pattern length 1000 characters), but on a billion-node label like HOSTNAME it falls back to a scan. Prefer STARTS WITH, ENDS WITH, or CONTAINS — all three are index-backed — and reserve regex for small, already-filtered result sets.

Variable-length traversals don't follow virtual edges — A pattern like [:ANNOUNCED_BY*1..3] will not work: ANNOUNCED_BY, LISTED_IN, ROUTES into ANNOUNCED_PREFIX, and BELONGS_TO into REGISTERED_PREFIX are synthesized at query time, and variable-length expansion only walks stored edges. Use fixed-length hops for those.

Global edge-count queries time out — A query that counts every edge in the graph (MATCH ()-[r]->() RETURN count(r)) will not finish. Use GET /api/query/stats for global node and edge counts — it answers instantly.

shortestPath needs a bounded length — Write shortestPath((a)-[*1..6]-(b)), not an unbounded search. Keep the upper bound modest; a high bound widens the search and slows the query.

UNWIND into CALL explain() is one backend call per item — Chaining UNWIND [...] AS x CALL explain(x) YIELD ... is supported and is the right way to score a list of indicators, but each item is a separate call to the threat-intelligence backend — keep the list short and expect the running time to grow with it.