Repro
const arr: any[] = [];
for (let i = 0; i < 30000; i++) {
arr.push({
id: i,
name: 'Record ' + i,
email: 'r' + i + '@example.com',
age: 18 + (i % 70),
addr: { street: i + ' Main St', city: 'Springfield', zip: 10000 + i },
});
}
const text = JSON.stringify(arr); // ← panic here
console.log('input bytes:', text.length);
Actual (Perry 0.5.30)
thread '<unnamed>' (...) panicked at crates/perry-runtime/src/json.rs:427:36:
byte index 3 is not a char boundary; it is inside '\0' (bytes 2..3) of
`� � �� � �X � � � �� � ��^I � �X
� � � �� � �� � �X � � � �� � �� � �X…`
Expected
Serializes the array, prints input bytes: 5444780 (or similar).
Observations
- Works cleanly up to ~20k records with this object shape.
- The panic message shows garbage bytes — the stringifier is reading past the valid UTF-8 range of a string payload. Probably a length / capacity confusion in
StringHeader, or a Buffer-vs-String header mix-up.
- The panic is in
perry-runtime/src/json.rs:427 calling .is_char_boundary() on a non-UTF-8 byte range.
- The threshold depends on object shape, not just record count — flat
{id, active} objects work at 500k records; adding nested .addr pushes the breakpoint down to ~30k.
Probably the same underlying data corruption as #42 (large Buffer param) and #[JSON.parse+iterate issue], just surfacing in the stringify path.
Impact
Any program that produces a JSON document larger than ~6 MB from TS-side data will hit this.
Repro
Actual (Perry 0.5.30)
Expected
Serializes the array, prints
input bytes: 5444780(or similar).Observations
StringHeader, or a Buffer-vs-String header mix-up.perry-runtime/src/json.rs:427calling.is_char_boundary()on a non-UTF-8 byte range.{id, active}objects work at 500k records; adding nested.addrpushes the breakpoint down to ~30k.Probably the same underlying data corruption as #42 (large Buffer param) and #[JSON.parse+iterate issue], just surfacing in the stringify path.
Impact
Any program that produces a JSON document larger than ~6 MB from TS-side data will hit this.