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

> Documentation Index
> Fetch the complete documentation index at: https://gistui.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Autofix and repair

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.

```tsx
<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](/docs/reference/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.

```ts
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.

```ts
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

```ts
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](/docs/reference/diagnostics/).

Source: https://gistui.com/docs/guides/autofix/index.mdx
