vercel-labs / vercel-labs/json-render

parseSpecStreamLine silently drops malformed JSON with no recovery attempt

Open Beginner friendly
#196 0 comments 0 reactions 0 assignees View on GitHub

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

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. 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

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.