XX-net/XX-Net · warning
query_dns_from_xxnet %s json:%s parse fail:%s
Error message
query_dns_from_xxnet %s json:%s parse fail:%s
What it means
query_dns_from_xxnet() got a 200 response but failed while parsing: it either couldn't json-decode the body or couldn't split the 'ip|cn' entries in the returned list. The warning includes the domain, the raw content, and the parse exception, then returns an empty IP list.
Source
Thrown at code/default/smart_router/local/dns_query.py:129
if isinstance(content, memoryview):
content = content.tobytes()
content = utils.to_str(content)
try:
rs = json.loads(content)
ips = rs["ip"]
xlog.debug("query_dns_from_xxnet %s cost:%f return:%s", domain, t1 - t0, ips)
#if dns_type == 1:
# ips = [ip for ip in ips if "." in ip]
ips_out = []
for ip_cn in ips:
ip, cn = ip_cn.split("|")
ips_out.append(ip)
return ips_out
except Exception as e:
xlog.warn("query_dns_from_xxnet %s json:%s parse fail:%s", domain, content, e)
return []
class LocalDnsQuery():
def __init__(self, timeout=3):
self.timeout = timeout
self.waiters = lru_cache.LruCache(100)
self.dns_server = self.get_local_dns_server()
self.sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
self.sock6 = socket.socket(socket.AF_INET6, socket.SOCK_DGRAM)
self.sock.settimeout(1)
self.sock6.settimeout(1)
self.running = True
self.th = threading.Thread(target=self.dns_recv_worker, args=(self.sock,), name="dns_ipv4_receiver")
self.th.start()
View on GitHub (pinned to cfa5bc17b6)
Solutions
- Look at the 'json:' field in the log line to see the actual body — that immediately reveals whether it's an HTML interstitial, an API error message, or a format change.
- Verify you're on a current XX-Net version whose parser matches the current dns.xx-net.org response format.
- If a captive portal/proxy is intercepting, fix connectivity so the tunnel delivers the real payload.
- Report a format-change upstream and patch the parser to tolerate missing '|' (e.g. ip_cn.split('|')[0]).
Example fix
// before
for ip_cn in ips:
ip, cn = ip_cn.split("|")
ips_out.append(ip)
// after
for ip_cn in ips:
ip = ip_cn.split("|")[0]
if ip:
ips_out.append(ip) Defensive patterns
Strategy: try-catch
Validate before calling
# sanity-check contract before relying on the result ips = query_dns_from_xxnet(domain) assert isinstance(ips, list) and all(b'.' in ip or ':' in ip for ip in ips), 'bad DNS payload'
Try / catch
try:
ips = query_dns_from_xxnet(domain)
except Exception:
ips = []
if not ips:
ips = socket.gethostbyname_ex(domain)[2] # fallback to system resolution Prevention
- Treat a 200-but-unparseable body as an API contract change; log and fall back to system DNS.
- Make parsing defensive: split('|')[0], tolerate missing fields.
- Pin the XX-Net version to one matching the dns.xx-net.org response format; test after upgrading.
When it happens
Trigger: Calling query_dns_from_xxnet() when the remote DNS service returns an unexpected body — non-JSON text (error page, HTML), JSON without the expected list structure, or entries not containing the '|' separator, causing ValueError/KeyError/TypeError inside the parse block.
Common situations: The remote dns.xx-net.org API changed format (e.g. dropped the ip|cn pairing); a proxy/captive portal injecting an HTML page; truncated response body; encoding issues where utils.to_str produced garbled text.
Related errors
- login_session %s json error:%r
- api:%s parse json:%s fail:%r
- query_dns_from_xxnet fail status:%d, cost=%f
- remove local DNS server %s from upstream
- Servers could not be resolved, %r.
AI-assisted analysis of XX-net/XX-Net@cfa5bc17b6 (2026-08-27).
Data as JSON: /api/errors/9d31ea598387461b.
Report an issue: GitHub.