Skip to content

fix(sync): guard against parser errors in UDP message listener - #114

Merged
buffcode merged 1 commit into
masterfrom
fix/handle-parser-errors
Apr 21, 2026
Merged

fix(sync): guard against parser errors in UDP message listener#114
buffcode merged 1 commit into
masterfrom
fix/handle-parser-errors

Conversation

@buffcode

Copy link
Copy Markdown
Owner

Summary

  • Wrap NtpPacketParser.parse() inside the dgram "message" listener in a try/catch so a parse failure rejects the pending promise cleanly instead of escaping the callback.
  • No dependency bump: ntp-packet-parser stays at ^0.5.0. This is the consumer-side guard, landed independently ahead of the parser's input-validation change.
  • On parse failure the listener now sets hasFinished = true and calls reject(err), preserving the existing invariants around hasFinished, timeoutHandler, and client.close().

Context

An upcoming ntp-packet-parser release will add input validation to NtpPacketParser.parse() — it will throw TypeError on non-48-byte inputs or non-Buffer arguments. Without this guard, that throw would escape the dgram listener callback and surface as uncaughtException on the Node process. Worse, because the listener is the only path that clears timeoutHandler / sets hasFinished, the outer promise would hang until replyTimeout fires — and on Node processes without a global uncaughtException handler, the process would crash outright.

With this guard, a malformed UDP response produces a rejected promise that the existing retry loop in collectSamples already handles (it catches via .catch((e) => e) and filters Error results out of ntpResults).

Test plan

There are no existing tests in this repo, so yarn build (tsc) is the only automated gate, and it passes.

Manual repro against a local UDP server that returns a malformed response:

const dgram = require("dgram");
const server = dgram.createSocket("udp4");
server.on("message", (_, rinfo) =>
  server.send(Buffer.from("short"), rinfo.port, rinfo.address));
server.bind(1234);
// Then call new NtpTimeSync({...}).getTime() pointed at 127.0.0.1:1234.
// Before: uncaughtException + promise hang. After: promise rejects cleanly.

Note

This PR intentionally does NOT bump the ntp-packet-parser dependency. The parser-side change (input validation) will land in a separate release; this consumer-side guard is defensive and safe to ship first.

🤖 Generated with Claude Code

Wrap NtpPacketParser.parse() in try/catch inside the dgram message
listener so a parse failure rejects the pending promise cleanly
instead of escaping as an uncaughtException. This matters once the
parser lands input validation (it will throw TypeError on non-48-byte
or non-Buffer inputs); without this guard a single malformed UDP
response would crash the host Node process.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@buffcode buffcode self-assigned this Apr 21, 2026
@buffcode
buffcode merged commit 1cbb436 into master Apr 21, 2026
6 checks passed
@buffcode
buffcode deleted the fix/handle-parser-errors branch April 21, 2026 23:59
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