Troubleshooting
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 [email protected], import component schemas from zod/v4, not zod. OpenUI component definitions expect the Zod 4 schema objects, and the [email protected] package ships those under the zod/v4 subpath.
import { z } from "zod/v4";If you want one import path that works across [email protected] 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.
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.
// 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.
// 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.
// 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).
// 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.
// 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.
<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:
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 providednull-required: required prop explicitly set to nullinline-reserved: Query/Mutation used inline instead of top-leveltool-not-found: tool name not found in toolProvider (hint lists available tools)parse-failed: response produced no renderable rootparse-exception: parser crashed on malformed inputruntime-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.
<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:
- The
toolProviderprop is set on<Renderer /> - Your tool function or MCP server includes the tool name used in
Query("tool_name", ...) - The tool name in the code matches exactly (case-sensitive)