---
title: Troubleshooting
description: Common issues and fixes when working with OpenUI Lang and the Renderer.
---

## Library Definition Issues

### Why do I get "Component was defined with a Zod 3 schema"?

If you're on `zod@3.25.x`, import component schemas from `zod/v4`, not `zod`. OpenUI component definitions expect the Zod 4 schema objects, and the `zod@3.25` package ships those under the `zod/v4` subpath.

```ts
import { z } from "zod/v4";
```

If you want one import path that works across `zod@3.25.x` and `zod@4`, prefer `zod/v4`.

### Why does a prop show up as `any` in the generated prompt?

`defineComponent(...)` automatically names component schemas, but standalone helper schemas do not get a friendly prompt name by default. Tag those helper schemas explicitly.

```ts
const ActionExpression = z.any();
tagSchemaId(ActionExpression, "ActionExpression");
```

This only affects prompt signatures. Validation behavior stays the same.

## LLM Output Issues

### Why are extra arguments being dropped?

Providing more arguments than the schema defines causes the extra arguments to be dropped or the statement to fail validation.

```text
// Schema: z.object({ title: z.string(), subtitle: z.string() })

// Wrong — 3 args for a 2-prop schema
h1 = Header("Title", "Sub", "extra")

// Correct
h1 = Header("Title", "Sub")
```

### Why is a component not appearing?

The LLM may invent component names that are not in the library. The parser drops any statement that references an unknown component.

```text
// Wrong — "Paragraph" is not registered
text = Paragraph("Hello world")

// Correct — use the registered name
text = TextContent("Hello world")
```

### Can I nest children inline or do I need references?

Both are valid. Inline children or references work.

```text
// Inline
root = Stack([Header("Title"), TextContent("Body")])

// References
root = Stack([h, t])
h = Header("Title")
t = TextContent("Body")
```

### Why doesn't `Count(arr)` work?

Built-in functions require the `@` prefix. Use `@Count(arr)`, not `Count(arr)`.

```text
// Wrong
count = Count(items)

// Correct
count = @Count(items)
```

### Query or Mutation used inline

`Query` and `Mutation` must be top-level statements, not inside component arguments.

```text
// Wrong — Query inside Table
tbl = Table([Col("Title", Query("list", {}).rows.title)])

// Correct — Query as a separate statement
data = Query("list", {}, {rows: []})
tbl = Table([Col("Title", data.rows.title)])
```

---

## Renderer Issues

### How do I debug errors?

Use the `onError` callback. It receives structured errors from the parser, runtime, and query system. Each error includes a `source`, `code`, `message`, and often a `hint` with a suggested fix.

```tsx
<Renderer
  library={library}
  response={code}
  toolProvider={tp}
  onError={(errors) => {
    for (const e of errors) {
      console.log(`[${e.source}] ${e.message}`);
      if (e.hint) console.log(`Hint: ${e.hint}`);
    }
  }}
/>
```

If you don't provide `onError`, errors are logged to `console.warn` automatically.

For LLM self-correction loops, format the errors and send them back:

```tsx
onError={(errors) => {
  if (!errors.length) return;
  const msg = errors
    .map((e) => `[${e.source}] ${e.statementId ? `"${e.statementId}": ` : ""}${e.message}${e.hint ? `\nHint: ${e.hint}` : ""}`)
    .join("\n\n");
  sendToLLM(`Fix these errors:\n\n${msg}`);
}}
```

Error codes:

- `unknown-component`: component name not in the library (hint lists available components)
- `missing-required`: required prop not provided
- `null-required`: required prop explicitly set to null
- `inline-reserved`: Query/Mutation used inline instead of top-level
- `tool-not-found`: tool name not found in toolProvider (hint lists available tools)
- `parse-failed`: response produced no renderable root
- `parse-exception`: parser crashed on malformed input
- `runtime-error`: expression evaluation failed on a prop (falls back to raw value)
- `render-error`: React component threw during render (falls back to last good state)

### `onParseResult` vs `onError`

`onError` is for error handling. It collects errors from all layers (parser, runtime, queries) into a single callback.

`onParseResult` is for inspection. It gives you the raw parse tree: the AST, statement count, unresolved references, and orphaned statements. Use it when you need to see what the parser produced, not just what went wrong.

```tsx
<Renderer
  onParseResult={(result) => {
    if (result) {
      console.log("Statements:", result.meta.statementCount);
      console.log("Unresolved:", result.meta.unresolved);
      console.log("Orphaned:", result.meta.orphaned);
      console.log("Errors:", result.meta.errors);
    }
  }}
/>
```

### Why are props appearing in the wrong position?

The key order in your `z.object({...})` is the contract for positional arguments. If you changed the key order without regenerating the system prompt, the LLM's arguments will map incorrectly.

Call `library.toJSONSchema()` to see exactly what the parser expects, and compare it against what the LLM is generating.

### Why are parts of the UI missing?

The parser drops invalid portions and renders everything else. If parts of the UI are missing, check `onError` for `unknown-component` or `missing-required` errors.

### Why isn't my Query fetching data?

Check that:

1. The `toolProvider` prop is set on `<Renderer />`
2. Your tool function or MCP server includes the tool name used in `Query("tool_name", ...)`
3. The tool name in the code matches exactly (case-sensitive)
