XTLS/Xray-core · error
unexpected EOF
Error message
unexpected EOF
What it means
Thrown in the trojan UDP relay path (handleUDPPayload) when reading the next UDP payload from the client fails with something other than clean io.EOF. Each trojan UDP packet carries address+length framing; a mid-stream read error (reset, deadline, truncated frame) breaks the framing and the loop aborts with this wrapper.
Source
Thrown at proxy/trojan/server.go:281
}
})
defer udpServer.RemoveRay()
inbound := session.InboundFromContext(ctx)
user := inbound.User
var dest *net.Destination
requestDone := func() error {
for {
select {
case <-ctx.Done():
return nil
default:
mb, err := clientReader.ReadMultiBuffer()
if err != nil {
if errors.Cause(err) != io.EOF {
return errors.New("unexpected EOF").Base(err)
}
return nil
}
mb2, b := buf.SplitFirst(mb)
if b == nil {
continue
}
timer.Update()
destination := *b.UDP
currentPacketCtx := ctx
if inbound.Source.IsValid() {
currentPacketCtx = log.ContextWithAccessMessage(ctx, &log.AccessMessage{
From: inbound.Source,
To: destination,
Status: log.AccessAccepted,
Reason: "",View on GitHub (pinned to 7d214f8b09)
Solutions
- Confirm it happens at session end (client gone) — then it is benign noise
- If it happens mid-session, check for middleboxes/NAT truncating the long-lived trojan TCP connection and enable keepalives/heartbeat in client policy
- Verify client and server run compatible trojan-UDP framing versions
Defensive patterns
Strategy: try-catch
Try / catch
mb, err := clientReader.ReadMultiBuffer()
if err != nil {
if errors.Cause(err) == io.EOF {
return nil // clean end of UDP session
}
// client went away mid-framing: nothing recoverable, stop relaying
return errors.New("unexpected EOF").Base(err)
} Prevention
- Enable TCP keepalive on the trojan carrier so dead peers are detected cleanly as io.EOF
- Size client-side UDP session lifetimes below NAT timeout so sessions end before the carrier breaks
- Do not treat this as retriable: the trojan-UDP framing cannot resync after a partial read
When it happens
Trigger: clientReader.ReadMultiBuffer() returns a connection reset, an i/o timeout, or a truncated trojan-UDP frame while relaying UDP (e.g. DNS or gaming traffic) over the trojan TCP carrier.
Common situations: UDP-over-trojan clients that vanish mid-session (NAT timeout, app close without a clean close_notify), mobile network switches, or MTU/truncation issues corrupting the length prefix.
Related errors
- failed to transport all UDP request
- failed to transport all UDP response
- failed to transfer request
- failed to write response
- connection ends
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/dd7c087949139779.
Report an issue: GitHub.