Skip to content

Record the first unreadable packet to a file instead of logging every one - #38

Open
u9g wants to merge 1 commit into
mainfrom
bad-packet-fingerprint
Open

Record the first unreadable packet to a file instead of logging every one#38
u9g wants to merge 1 commit into
mainfrom
bad-packet-fingerprint

Conversation

@u9g

@u9g u9g commented Sep 8, 2026

Copy link
Copy Markdown
Owner

On a server whose particle registry does not match the client's (Jartex's lobby sends 1.21.3 ids to a 1.21.4 client, so item_slime arrives as trail with no payload), every such packet printed a full PartialReadError stack. One bot logged 14,814 of them, most of a 30 MB log, and none of them carried the bytes.

Why the bytes were never available

protodef's FullPacketParser._transform swallows partial reads: console.log(e.stack), then cb() with nothing emitted and no buffer attached. bot.on('error') never sees these. The only place the packet buffer is still in hand is parsePacketBuffer, so the daemon wraps that on each deserializer.

What it does now

The first unreadable packet is written to bad-packet.json in the daemon's working directory, then nothing else is written or printed:

{
  "bot":     { "name", "host", "port", "username", "version" },
  "state":   "play",
  "packet":  { "id": 42, "name": "world_particles" },
  "problem": "partial-read",
  "length":  51,
  "sha256":  "...",
  "bytes":   "<hex of the whole packet>",
  "error":   { "name": "PartialReadError", "message", "stack" }
}

problem is partial-read when the definitions expect more bytes than arrived, or trailing-bytes (with read) when they stop short of the end. The second case parses "successfully" and is otherwise invisible; it is the mirror image of the same registry mismatch.

At most one file: a process flag stops retries, and the file is opened with wx, so a restart in the same directory leaves the first one in place. noErrorLogging is set on the deserializer instance rather than via hideErrors, which would also mute mineflayer's _warn. nmp builds a fresh deserializer per protocol state, so the wrap is reapplied on 'state'.

Testing

Against the same minecraft-protocol / protodef@1.19.0 / minecraft-data 3.116.0-allprs build the affected bot ran, with a world_particles/trail packet truncated to the Jartex shape:

  • an unwrapped deserializer prints the stack; the wrapped one prints nothing
  • one file, with problem: partial-read, packet: world_particles 0x2a, the exact 51 bytes and matching sha256
  • second and third bad packets: no write, file unchanged
  • 'state' event: the replacement deserializer is wrapped
  • a valid packet writes nothing; the same packet plus 3 stray bytes writes trailing-bytes with read: 77 of 80
  • a fresh process in the same directory: no callback, first file untouched

npx tsc --noEmit reports only the pre-existing createHuman error from this checkout's stale node_modules.

… one

protodef swallows a PartialReadError inside FullPacketParser._transform:
it prints the stack and drops the packet without emitting anything, so
bot.on('error') never sees it and the raw bytes are never attached. On a
server whose particle registry does not match the client's, that printed
14,814 stacks into one bot's log.

The daemon now wraps each deserializer's parsePacketBuffer, where the
packet buffer is still in hand, and writes the first failure to
bad-packet.json in the working directory: the exact bytes, their sha256,
the packet id and name, the protocol state, and the error. Trailing bytes
after a successful read are recorded the same way, since a payload the
definitions think is shorter than it is fails silently otherwise.

At most one file is written: a process flag stops retries, and wx leaves
an existing file alone across restarts. noErrorLogging is set on the
deserializer instance rather than through hideErrors, which would also
silence mineflayer's own warnings. nmp builds a new deserializer per
protocol state, so the wrap is reapplied on the state event.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant