{"record":{"id":"82fb8d597403308f","repo":"XTLS/Xray-core","slug":"failed-to-write-raw-udp-packet-back-to-stack","errorCode":null,"errorMessage":"failed to write raw udp packet back to stack","messagePattern":"failed to write raw udp packet back to stack","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"proxy/tun/stack_gvisor.go","lineNumber":187,"sourceCode":"\t\t\tSrcAddr:     srcIP,\n\t\t\tDstAddr:     dstIP,\n\t\t})\n\t\tipHdr.SetChecksum(^ipHdr.CalculateChecksum())\n\t} else {\n\t\tipHdr := header.IPv6(pkt.NetworkHeader().Push(header.IPv6MinimumSize))\n\t\tipHdr.Encode(&header.IPv6Fields{\n\t\t\tPayloadLength:     uint16(udpLen),\n\t\t\tTransportProtocol: header.UDPProtocolNumber,\n\t\t\tHopLimit:          64,\n\t\t\tSrcAddr:           srcIP,\n\t\t\tDstAddr:           dstIP,\n\t\t})\n\t}\n\n\t// dispatch the packet\n\terr := t.stack.WriteRawPacket(defaultNIC, ipProtocol, buffer.MakeWithView(pkt.ToView()))\n\tif err != nil {\n\t\treturn errors.New(\"failed to write raw udp packet back to stack\", err)\n\t}\n\n\treturn nil\n}\n\n// Close is called by Handler to shut down the stack\nfunc (t *stackGVisor) Close() error {\n\tif t.stack == nil {\n\t\treturn nil\n\t}\n\tt.endpoint.Attach(nil)\n\tt.stack.Close()\n\tfor _, endpoint := range t.stack.CleanupEndpoints() {\n\t\tendpoint.Abort()\n\t}\n\n\treturn nil\n}","sourceCodeStart":169,"sourceCodeEnd":205,"githubUrl":"https://github.com/XTLS/Xray-core/blob/7d214f8b094f75322fa3990f8aadad1c912f24f5/proxy/tun/stack_gvisor.go#L169-L205","documentation":"The gVisor netstack rejected WriteRawPacket when the tun handler tried to inject a synthesized UDP/IP packet back toward the client (defaultNIC). The packet was rebuilt with a fresh IPv4/IPv6 header (checksum recomputed, hop limit 64) and pushed into the stack; rejection means the stack considered the NIC invalid or the packet malformed.","triggerScenarios":"The reply path after a tun-dispatched UDP flow: stack.WriteRawPacket returns non-nil because the NIC/endpoint was detached (Close racing an in-flight reply), the packet buffer exceeded NIC MTU, or the stack was already shutting down.","commonSituations":"Sessions torn down while DNS/UDP replies are still arriving (shutdown or interface flap), MTU mismatch between tun mtu config and packets produced, or gVisor version behavior changes on WriteRawPacket error semantics.","solutions":["If seen around shutdown/restart of the tun interface, treat as benign teardown race","Verify the tun inbound's mtu matches the system interface (e.g. 1500/9000) and no offload mismatch","Upgrade xray-core: WriteRawPacket error handling in the tun path has had fixes across versions","Capture on the tun device to confirm outbound replies otherwise flow normally"],"exampleFix":"// json: align mtu with the created interface\n\"inbounds\": [{\n  \"protocol\": \"tun\",\n  \"settings\": {\"mtu\": 9000, \"address\": [\"172.19.0.1/30\", \"fdfe:dcba:9876::1/126\"]}\n}]","handlingStrategy":"try-catch","validationCode":"// check NIC liveness before injecting the reply packet\nif t.stack == nil {\n    return errors.New(\"stack closed\")\n}\n// optionally guard with the endpoint's attached state if exposed by your gVisor version","typeGuard":null,"tryCatchPattern":"if err := t.stack.WriteRawPacket(defaultNIC, ipProtocol, buffer.MakeWithView(pkt.ToView())); err != nil {\n    // reply raced stack shutdown or MTU bounds: drop the packet, do not crash the handler\n    return errors.New(\"failed to write raw udp packet back to stack\", err)\n}","preventionTips":["Treat failures during interface teardown as races; drain UDP workers before Close","Keep tun mtu in config equal to the real interface MTU and disable conflicting offloads","Stay current with xray-core releases; gVisor tun internals change error semantics between versions"],"tags":["tun","gvisor","udp","packet-injection"],"backgroundTag":null,"analyzedSha":"7d214f8b094f75322fa3990f8aadad1c912f24f5","analyzedAt":"2026-08-15T14:26:24.325Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}