Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Next Next commit
fix: throw when encoding a BigInt outside int64/uint64 range
With `useBigInt64: true`, `encodeBigInt64` branched only on sign and
called the native `DataView.setBigUint64`/`setBigInt64`, which truncate
mod 2^64 without throwing. A BigInt outside the representable range was
silently corrupted on encode:

    encode(2n ** 64n,        { useBigInt64: true }) // decoded as 0n
    encode(2n ** 64n + 1n,   { useBigInt64: true }) // decoded as 1n
    encode(-(2n ** 63n) - 1n,{ useBigInt64: true }) // sign flip: 9223372036854775807n

The MessagePack int family is fixed 64-bit, so the only representable
range is the union of int64 and uint64: [-2^63, 2^64 - 1]. Add a range
check that throws for anything outside it, matching how the encoder
already rejects other unrepresentable inputs (too-long strings, too-large
arrays/maps/binaries).
  • Loading branch information
spokodev committed Jun 30, 2026
commit 7543d04fbe5044324cb87fd0e3c4ee2215271f55
3 changes: 3 additions & 0 deletions src/Encoder.ts
Original file line number Diff line number Diff line change
Expand Up @@ -291,6 +291,9 @@ export class Encoder<ContextType = undefined> {
}

private encodeBigInt64(object: bigint): void {
if (object < -(BigInt(2) ** BigInt(63)) || object > BigInt(2) ** BigInt(64) - BigInt(1)) {

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
private encodeBigInt64(object: bigint): void {
if (object < -(BigInt(2) ** BigInt(63)) || object > BigInt(2) ** BigInt(64) - BigInt(1)) {
const INT64_MIN = -(BigInt(2) ** BigInt(63));
const UINT64_MAX = BigInt(2) ** BigInt(64) - BigInt(1);
private encodeBigInt64(object: bigint): void {
if (object < INT64_MIN || object > UINT64_MAX) {

throw new Error(`Cannot encode BigInt as int64/uint64 because it is out of range: ${object}`);
}
if (object >= BigInt(0)) {
// uint 64
this.writeU8(0xcf);
Expand Down
26 changes: 26 additions & 0 deletions test/bigint64.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -35,4 +35,30 @@ describe("useBigInt64: true", () => {
const encoded = encode(value, { useBigInt64: true });
assert.deepStrictEqual(decode(encoded, { useBigInt64: true }), value);
});

it("round-trips the boundary values of int64/uint64", () => {
const values = [
BigInt(0),
BigInt(42),
BigInt(2) ** BigInt(63) - BigInt(1), // max int64
-(BigInt(2) ** BigInt(63)), // min int64
BigInt(2) ** BigInt(64) - BigInt(1), // max uint64
];
for (const value of values) {
const encoded = encode(value, { useBigInt64: true });
assert.deepStrictEqual(decode(encoded, { useBigInt64: true }), value);
}
});

it("throws when a bigint is out of the int64/uint64 range", () => {
const values = [
BigInt(2) ** BigInt(64), // uint64 max + 1
BigInt(2) ** BigInt(64) + BigInt(1),
-(BigInt(2) ** BigInt(63)) - BigInt(1), // int64 min - 1
-(BigInt(2) ** BigInt(100)),
];
for (const value of values) {
assert.throws(() => encode(value, { useBigInt64: true }), /out of range/);
}
});
});