{"record":{"id":"c16e6776aa323b93","repo":"XTLS/Xray-core","slug":"unable-to-get-destination","errorCode":null,"errorMessage":"unable to get destination","messagePattern":"unable to get destination","errorType":"exception","errorClass":"errors.Error","httpStatus":null,"severity":"error","filePath":"proxy/dokodemo/dokodemo.go","lineNumber":135,"sourceCode":"\t\t\tif ob.Target.IsValid() {\n\t\t\t\tdest = ob.Target\n\t\t\t\tdestinationOverridden = true\n\t\t\t}\n\t\t}\n\t\tiConn := stat.TryUnwrapStatsConn(conn)\n\t\tif tlsConn, ok := iConn.(tls.Interface); ok && !destinationOverridden {\n\t\t\tif serverName := tlsConn.HandshakeContextServerName(ctx); serverName != \"\" {\n\t\t\t\tdest.Address = net.DomainAddress(serverName)\n\t\t\t\tdestinationOverridden = true\n\t\t\t\tctx = session.ContextWithMitmServerName(ctx, serverName)\n\t\t\t}\n\t\t\tif tlsConn.NegotiatedProtocol() != \"h2\" {\n\t\t\t\tctx = session.ContextWithMitmAlpn11(ctx, true)\n\t\t\t}\n\t\t}\n\t}\n\tif !dest.IsValid() || dest.Address == nil {\n\t\treturn errors.New(\"unable to get destination\")\n\t}\n\n\tinbound := session.InboundFromContext(ctx)\n\tinbound.Name = \"dokodemo-door\"\n\tinbound.CanSpliceCopy = 1\n\tinbound.User = &protocol.MemoryUser{\n\t\tLevel: d.config.UserLevel,\n\t}\n\n\tctx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{\n\t\tFrom:   conn.RemoteAddr(),\n\t\tTo:     dest,\n\t\tStatus: log.AccessAccepted,\n\t\tReason: \"\",\n\t})\n\terrors.LogInfo(ctx, \"received request for \", conn.RemoteAddr())\n\n\tvar reader buf.Reader","sourceCodeStart":117,"sourceCodeEnd":153,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/proxy/dokodemo/dokodemo.go#L117-L153","documentation":"The dokodemo-door process callback derives the real destination either from config (address), from UDP session targets, or from the original-destination info on the connection (SO_ORIGINAL_DST / recvfrom). If none yields a valid destination (dest invalid or nil address), processing aborts with this error. This is the classic transparent-proxy failure: the inbound cannot discover where the intercepted connection originally headed.","triggerScenarios":"Using dokodemo with followRedirect/followOriginalDestination but traffic reaches it via plain DNAT/REDIRECT without proper iptables/nftables REDIRECT target, no TPROXY mark, or a protocol (e.g. ICMP) exposing no original destination; also \"address\" left empty when not following destination.","commonSituations":"Missing or wrong iptables rules (NAT table REDIRECT vs mangle TPROXY), Docker NAT'd traffic losing original dst, IPv6 traffic not covered by the rules, or misordered rules after firewall refresh; TLS serverName extraction path (seen in source) only applies when MITM is active.","solutions":["Verify redirect rules exist and match the traffic: iptables -t nat -L -n / nft list ruleset; REDIRECT for nat path, TPROXY with mangle for tproxy path","Ensure the dokodemo inbound option matches the redirection method (followRedirect vs tproxy setting)","If you do not need original-destination, set an explicit \"address\" (and \"port\") in inbound settings so dest is always known","Cover IPv6 (ip6tables/nft inet family) if clients connect over v6"],"exampleFix":"# before: plain DNAT loses original destination\niptables -t nat -A PREROUTING -p tcp -d $EXT_IP --dport 80 -j DNAT --to-destination 127.0.0.1:1080\n\n# after: REDIRECT preserves it for followRedirect\niptables -t nat -A PREROUTING -p tcp -i eth0 --dport 80 -j REDIRECT --to-ports 1080","handlingStrategy":"validation","validationCode":"// pre-flight: ensure original-destination retrieval works for this traffic\nif followRedirect {\n    if !redirectRulesExist(\"nat\") { fatal(\"missing iptables REDIRECT rules\") }\n} else if settings.Address == nil {\n    fatal(\"dokodemo needs address or original-destination mode\")\n}","typeGuard":null,"tryCatchPattern":"if err := d.process(ctx, link, dialer); err != nil {\n    if err.Error() == \"unable to get destination\" { logCaptureHintAndDrop(ctx); return nil }\n    return err\n}","preventionTips":["Keep iptables/nftables redirect rules in a managed, idempotent script applied before Xray starts","Cover both IPv4 and IPv6 with matching rules","When original-destination is unreliable (containers/NAT), set explicit address/port in dokodemo settings","Re-apply firewall rules after any firewall refresh before blaming Xray"],"tags":["go","xray","dokodemo","transparent-proxy","iptables","network"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T17:31:12.345Z"}