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

  1. Confirm it happens at session end (client gone) — then it is benign noise
  2. If it happens mid-session, check for middleboxes/NAT truncating the long-lived trojan TCP connection and enable keepalives/heartbeat in client policy
  3. 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

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


AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15). Data as JSON: /api/errors/dd7c087949139779. Report an issue: GitHub.