iMessage plugin: parseAttributedBody truncates messages >127 bytes on macOS 15
Bug
The iMessage plugin's parseAttributedBody function in server.ts incorrectly parses the Apple typedstream length encoding for messages longer than 127 bytes. On macOS 15 (Sequoia), where the text column in chat.db is frequently null and message content is stored only in attributedBody, this causes most non-trivial messages to be silently truncated.
Root cause
The typedstream format uses a multi-byte length prefix when the string exceeds 127 bytes:
< 0x80: literal length (single byte)0x81: 2-byte little-endian uint16 length follows0x82: 3-byte little-endian uint24 length follows0x83: 4-byte little-endian uint32 length follows
The current code treats 0x81 as "1 byte follows", 0x82 as "2 bytes follow", etc. — off by one in every case:
// BUGGY
if (b === 0x81) { len = buf[i]; i += 1 }
else if (b === 0x82) { len = buf.readUInt16LE(i); i += 2 }
else if (b === 0x83) { len = buf.readUIntLE(i, 3); i += 3 }
For a 264-byte message, 0x81 is followed by 0x08 0x01 (uint16LE = 264). The buggy code reads only 0x08 → len=8, truncating the message to 8 bytes.
Fix
if (b === 0x81) { len = buf.readUInt16LE(i); i += 2 }
else if (b === 0x82) { len = buf.readUIntLE(i, 3); i += 3 }
else if (b === 0x83) { len = buf.readUInt32LE(i); i += 4 }
Reproduction
- Run the iMessage plugin on macOS 15 (Darwin 24.x)
- Send any iMessage > 127 bytes to yourself
- The message arrives truncated (typically 5-10 visible characters)
Verified by querying chat.db directly — the full message is in attributedBody, and the correct length (264) is recoverable with uint16LE parsing.
Environment
- macOS 15 (Darwin 24.6.0)
- Plugin:
imessage@claude-plugins-officialv0.0.1 - File:
external_plugins/imessage/server.ts,parseAttributedBody()around line 91
Happy to submit a PR if pointed to the right repo.
This issue has 2 comments on GitHub. Read the full discussion on GitHub ↗