XTLS/Xray-core · error
write packet: bad length: %d
Error message
write packet: bad length: %d
What it means
encodePacket rejects an outgoing packet whose encoded field data exceeds maxPacketDataLength (32 KiB, protocol.go:11). This is a hard cap on the Minecraft protocol frame body this transport will emit, guarding the framing layer from oversized packets. It fires before the packet ID is prepended and before framing.
Source
Thrown at transport/internet/finalmask/xmc/protocol.go:404
return 0, err
}
if err = writeFull(w, frame); err != nil {
return 0, fmt.Errorf("write packet data: %w", err)
}
return len(frame), nil
}
func encodePacket(packetID int, fields ...field) ([]byte, error) {
var dataBuf bytes.Buffer
for _, field := range fields {
err := field.writeTo(&dataBuf)
if err != nil {
return nil, fmt.Errorf("write packet field: %w", err)
}
}
if dataBuf.Len() > maxPacketDataLength {
return nil, fmt.Errorf("write packet: bad length: %d", dataBuf.Len())
}
packetIDVarint := Varint(packetID)
bodyLength := varintSize(packetIDVarint) + dataBuf.Len()
if bodyLength > maxPacketBodyLength {
return nil, fmt.Errorf("write packet: bad length: %d", bodyLength)
}
var frame bytes.Buffer
frame.Grow(varintSize(Varint(bodyLength)) + bodyLength)
frameLength := Varint(bodyLength)
if err := frameLength.writeTo(&frame); err != nil {
return nil, fmt.Errorf("write packet length: %w", err)
}
if err := packetIDVarint.writeTo(&frame); err != nil {
return nil, fmt.Errorf("write packet ID: %w", err)
}
frame.Write(dataBuf.Bytes())View on GitHub (pinned to 7d214f8b09)
Solutions
- Check the wrapped length in the message and identify which field (usually profile textures) exceeds 32 KiB
- Shorten or omit the textures property in the configured login profile (empty TexturesValue/TexturesSignature)
- If genuinely large payloads are required, raise maxPacketDataLength in a fork — but note vanilla clients may reject such frames
- Validate profile sizes at config load time instead of at connection time
Example fix
// before
profile := loginProfile{TexturesValue: hugeBase64} // > 32 KiB
// after
profile := loginProfile{} // omit textures; empty value stays within the frame cap Defensive patterns
Strategy: validation
Validate before calling
const maxFieldBudget = 32 * 1024\nif len(profile.TexturesValue)+len(profile.TexturesSignature) > maxFieldBudget-1024 {\n\treturn fmt.Errorf("login profile textures too large: %d bytes", len(profile.TexturesValue)+len(profile.TexturesSignature))\n} Type guard
func profileFitsPacket(p loginProfile) bool {\n\treturn len(p.TexturesValue)+len(p.TexturesSignature) < 30*1024\n} Try / catch
if err := writePacket(w, 0x02, fields...); err != nil {\n\tvar maxErr *maxPacketError\n\tif errors.As(err, &maxErr) || strings.Contains(err.Error(), "bad length") {\n\t\t// shrink payload (drop textures) and retry once\n\t}\n} Prevention
- Validate login profile texture sizes at config load, not per connection
- Keep status JSON and any custom strings well under 32 KiB
- Add unit tests asserting every packet you construct stays under maxPacketDataLength
When it happens
Trigger: Calling writePacket with fields whose combined serialized size exceeds 32768 bytes, e.g. writing the login-finished success packet with a very large textures value/signature (profile.TexturesValue/TexturesSignature) or an oversized status JSON string.
Common situations: Injecting a login profile whose base64 textures property is larger than 32 KiB; hand-edited or machine-generated profiles from config. Vanilla servers keep textures well under this limit, so seeing it usually means misconfigured profile data.
Related errors
- write packet field: %w
- write packet length: %w
- write packet ID: %w
- not a Service.
- Dispatcher: Invalid destination.
AI-assisted analysis of XTLS/Xray-core@7d214f8b09 (2026-08-15).
Data as JSON: /api/errors/8784f3392f9b2480.
Report an issue: GitHub.