vercel-labs / vercel-labs/json-render
parseSpecStreamLine silently drops malformed JSON with no recovery attempt
Nobody has claimed this yet.
- Dominant language
- TypeScript
- Stars
- 16.8k
- Forks
- 901
- Avg merge
- 3h 14m
- Merged PRs (30d)
- 4
Description
Problem
parseSpecStreamLine in @json-render/core silently drops any line where JSON.parse fails, with no attempt at recovery:
function parseSpecStreamLine(line) {
const trimmed = line.trim();
if (!trimmed || !trimmed.startsWith("{")) return null;
try {
const patch = JSON.parse(trimmed);
if (patch.op && patch.path !== void 0) {
return patch;
}
return null;
} catch {
return null; // ← silently dropped, no recovery
}
}
Since this package is specifically designed to parse AI-generated JSONL output, it should be resilient to the most common LLM generation error: brace miscounting.
Real-world example
When asking Claude Sonnet 4.6 to generate a dashboard with MetricTile, BarChart, PieChart, and DataTable components (all using dataSource with a nested {sql: "..."} object), 7 out of 12 spec lines were generated with exactly one extra trailing }:
// What the model generated (INVALID — extra closing brace):
{"op":"add","path":"/elements/metric-payments","value":{"type":"MetricTile","props":{"title":"Payments Collected","format":"currency","dataSource":{"sql":"SELECT ..."}}},"children":[]}}
// What it should have generated (VALID):
{"op":"add","path":"/elements/metric-payments","value":{"type":"MetricTile","props":{"title":"Payments Collected","format":"currency","dataSource":{"sql":"SELECT ..."}},"children":[]}}
The pattern is consistent: every element with a dataSource property (which adds an extra level of {} nesting inside props) confuses the model's brace tracking. The children field ends up outside the value object with an extra } appended.
Result: Only the page header card rendered. All 4 metric tiles, both charts, and the data table were silently dropped — making the dashboard appear completely broken with no error feedback.
Suggested fix
Add a simple recovery step when JSON.parse fails — try trimming trailing } characters one at a time:
function parseSpecStreamLine(line) {
const trimmed = line.trim();
if (!trimmed || !trimmed.startsWith("{")) return null;
try {
const patch = JSON.parse(trimmed);
if (patch.op && patch.path !== void 0) return patch;
return null;
} catch {
// Recovery: try removing extra trailing braces (common LLM error)
let attempt = trimmed;
while (attempt.endsWith("}")) {
attempt = attempt.slice(0, -1);
try {
const patch = JSON.parse(attempt);
if (patch.op && patch.path !== void 0) return patch;
} catch {
continue;
}
}
return null;
}
}
This would have recovered all 7 broken lines in the example above. A more comprehensive approach could also handle missing trailing braces (try appending }) or other common malformations.
Additional suggestion
Consider emitting a warning or callback when recovery is needed (or when a line is ultimately dropped), so developers can observe generation quality and debug rendering issues more easily. Currently the silent return null makes these failures invisible.
Environment
@json-render/core@0.12.0- Model: Claude Sonnet 4.6 via Cloudflare AI Gateway
- Streaming mode with
createMixedStreamParser
Contributor guide
No contributing guide indexed for this repository
First steps
- Read the whole issue, then the project's contributing guide.
- Comment on the issue to say you are picking it up — it saves two people doing the same work.
- Fork the repository and make your change on a branch.
- Open a pull request that references the issue number.
Research direction
Start with parseSpecStreamLine in @json-render/core and trace how createMixedStreamParser handles lines that return null. Verify the malformed JSONL examples with extra trailing braces are recovered while invalid or non-patch lines remain ignored. Done means the affected dashboard patches are parsed instead of silently dropped, with any recovery warning or callback behavior matching the issue's chosen scope.
Written by the indexing model from the issue text.
Assessment
- Tech stack
- typescript
- Domain
- tooling
- Issue type
- Bug
- Difficulty
- 2/5
- Estimated time
- 1-3 hours
- Activity status
- Stale
- Clarity
- Clearly specified
- Newbie friendliness
- 68/100