What happens. The SSE reader decodes each network chunk to text on its own with String::from_utf8_lossy. When a chunk boundary falls inside a multi-byte character, both halves become U+FFFD. The JSON around them still parses, so the query service quietly keeps the damaged text or emoji.
How to reproduce. crates/vibescrobble-index/src/sse.rs:119: pending.push_str(&String::from_utf8_lossy(&chunk)). The query service mirrors the index's /events through this reader (vibescrobble-query/src/lib.rs:49). A local test served event: scrobble\ndata: {"emoji":"🧪"}\n\n in two writes, cut two bytes into the emoji, to read_frames, and the frame's data came back as {"emoji":"���"}.
Why it matters. Nearly every scrobble has an emoji, and many have non-Latin text. A large event, such as a long line or a work event naming groups, will sooner or later split inside one. The query's badges, cards, share pages and /view then show the broken character until the next reseed, and nothing logs it.
Fixed by https://tangled.org/permadeath.com/vibescrobble.com/pulls/349, now on main.