Skip to content

Autofix and repair

How GistUI repairs a model's mistakes in code, what it changes, and how to add a model-assisted repair on the server.

Updated View as Markdown

Models make small mistakes: a prop that does not exist, an enum value that is close but wrong, a reference nobody defined. GistUI repairs them in code, with no model call, and shows the repaired UI in place.

What gets repaired

Mistake Repair
Unknown prop, extra argument Removed
Invalid value on an optional prop Removed, so the default applies
Invalid value on a required prop The closest allowed value
Missing required id or name Filled from the call’s own text or its id
Reference that misses a statement by case or one letter (MetricsRow, incidentsTable) Points at that statement
Reference to something never defined The reference is removed
A section defined but never used Added to the root’s children
A data table defined but never used Shown in a Table on the root
A table written inside a call (Table(|A|B|, |1|2|)) Moved to its own statement, every row kept
Curly quotes used as quotes, a component named without () Read as meant
One unreadable argument ("Today is " + new Date(), a stray ?) Its text is kept, or the argument is dropped; the rest of the statement stays
No root statement, several sections A root is added that holds them all
Unknown component Renamed when it is clearly a typo (Txt to Text); otherwise a plain container that keeps its children
A component whose required data is missing, or a cycle That statement is removed
Syntax slips: single quotes, key=value, a missing closing bracket, a trailing comma Read as meant, while streaming

The last row happens during parsing: the format defines a numbered recovery rule for each slip, so every implementation reads imperfect text the same way. The others happen once, when the stream ends.

In the renderer

autofix is on by default in every renderer. onAutofix reports what changed.

<GistUI
  library={ui}
  stream={stream}
  onAutofix={({ changes, errors }) => console.log(changes)} // changes: string[]
/>

The repair code loads on demand, only when a program actually needs it, and only what changed re-renders. autofix={false} turns it off.

How much it helps

On the public benchmark (46 screens × 4 runs), autofix takes validity to 100% with gpt-oss-120b and to 99.5% with gpt-5-nano. Checking and repairing a screen takes 1–2 ms at the median and 7 ms at the slowest. OpenUI repairs with a second model call in OpenUI Cloud; GistUI’s repair is code in the library. No repaired screen lost a component.

Model As written After autofix
gpt-oss-120b 87.9% 100%
gpt-5-nano 22.8% 99.5%

Details in Benchmarks.

On the server

Use @gistui/server when the stored or logged answer should be the repaired one, or when you want to validate before sending.

import { library } from "@gistui/catalog";
import { repairProgram, repairStream, toReadable, validateProgram } from "@gistui/server";

validateProgram(text, library); // { valid, errors, fixed }

// A finished program:
const { valid, source, patch, errors, fixed } = await repairProgram(text, library);

// A stream: passes the text through, appends repairs as edits at the end.
return new Response(toReadable(repairStream(textStream, library)));

Asking the model to fix the rest

Code cannot repair everything: a required prop whose value only the model knows, for example. Give repairProgram or repairStream a complete function and it asks your model to fix only the statements that still fail, with a small prompt.

const result = await repairProgram(text, library, {
  complete: async ({ system, user }) => {
    const r = await generateText({ model, system, prompt: user });
    return r.text;
  },
});
  • Deterministic repair always runs first. The model is asked only if errors remain.
  • A model round is kept only when it leaves fewer errors than before.
  • The program and its errors are sent inside <program> and <errors> tags and described as data, so text inside the program cannot pass for instructions.

With @gistui/core directly

import { library } from "@gistui/catalog";
import { autofix, parse } from "@gistui/core";

const r = parse(source, library);
r.valid.strict;  // no diagnostics at all
r.valid.lenient; // every problem was repaired by a rule
r.errors;        // [{ code, stmtId, line, message, hint }]

const fixed = autofix(source, library); // the repaired program and the list of changes

Every diagnostic code is listed in Diagnostics.

Navigation

Type to search…

↑↓ navigate↵ selectEsc close