XTLS/Xray-core · error

bad login finished packet id: %d

Error message

bad login finished packet id: %d

What it means

After encryption, the server sent a login-state packet whose ID is neither 0x00 (disconnect) nor 0x02 (Login Success). The handshake state machine only accepts those two IDs at this point, so any other ID (e.g. 0x03 Login Acknowledged, 0x04/0x05 compression or plugin packets) is a protocol violation or version skew.

Source

Thrown at transport/internet/finalmask/xmc/client.go:206

	c.writer, err = newCryptoWriter(c.writer, sharedSecret)
	if err != nil {
		return fmt.Errorf("new crypto writer: %w", err)
	}

	pkt, err = readPacket(c.reader)
	if err != nil {
		return fmt.Errorf("read login finished: %w", err)
	}
	if pkt.packetID == 0x00 {
		var reason String
		if readErr := pkt.readFields(&reason); readErr != nil {
			return fmt.Errorf("authentication rejected")
		}
		return fmt.Errorf("authentication rejected: %s", reason)
	}
	if pkt.packetID != 0x02 {
		return fmt.Errorf("bad login finished packet id: %d", pkt.packetID)
	}

	receivedProfile, err := readLoginSuccess(pkt)
	if err != nil {
		return fmt.Errorf("read login finished fields: %w", err)
	}
	if receivedProfile != selectedProfile {
		return fmt.Errorf("login profile mismatch")
	}
	loginAcknowledgedLength, err := writePacketWithLength(c.writer, 0x03)
	if err != nil {
		return fmt.Errorf("write login acknowledged: %w", err)
	}
	if err = runPaddingSchedule(c.reader, c.writer, true, loginAcknowledgedLength, c.paddingSchedule); err != nil {
		return fmt.Errorf("run startup padding: %w", err)
	}

	packet := newPacketStream(c.reader, c.writer, true)

View on GitHub (pinned to 7d214f8b09)

Solutions

  1. Confirm the destination is the paired xmc server, not a vanilla Minecraft server
  2. Use matching client/server builds of the finalmask/xmc package
  3. Check the server's protocol version handling around login; packet 0x01 here usually means the server tried a second Encryption Request
  4. Report upstream if both sides are the same version and it persists
Defensive patterns

Strategy: validation

Validate before calling

// before connecting, verify both ends run the same xmc build
if clientBuildVersion != serverBuildVersion {
    return fmt.Errorf("xmc version skew: client %s vs server %s", clientBuildVersion, serverBuildVersion)
}

Try / catch

_, err := conn.Read(buf)
if err != nil && strings.Contains(err.Error(), "bad login finished packet id") {
    return fmt.Errorf("protocol mismatch: endpoint is not a matching xmc server")
}

Prevention

When it happens

Trigger: First Read/Write on the wrapped conn when the server runs a different protocol/handshake ordering than this client expects (protocol version 775 hardcoded at client.go:84), or a non-xmc/genuine Minecraft server responded because the port points at vanilla software.

Common situations: Pointing the outbound at a plain Minecraft server instead of the xmc-compatible endpoint; server or client built from mismatched finalmask/xmc versions; a proxy in front injecting extra login packets.

Related errors


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