Ask for a schema, not prose

Pass Output.object({ schema }) instead of parsing the model's text.

AI SDK docsai-sdk.dev
The schema is also used to validate the generated data, ensuring type safety and correctness.
AI SDK Core · Structured DataRead docs ↗

From ai-sdk and the Generating Structured Data docs.

Three invoices, three replies

Press Extract 3 invoices and see what gets saved.

JSON.parse(text)Bad
    Saved right 0Saved wrong 0Crashed 0
    const invoice = JSON.parse(text)
    Output.objectGood
      Saved right 0Saved wrong 0Caught 0
      output: Output.object({ schema: invoice })

      import { generateText, Output, NoObjectGeneratedError } from 'ai';
      import { z } from 'zod';
      
      const invoice = z.object({
        vendor: z.string(),
        total: z.number().describe('Grand total in USD'),
        dueDate: z.string().describe('ISO date, YYYY-MM-DD'),
      });
      
      try {
        const { output } = await generateText({
          model: 'openai/gpt-5.5',
          output: Output.object({ schema: invoice }),
          prompt: `Extract the invoice fields:\n${pdfText}`,
        });
        await saveInvoice(output); // typed { vendor, total, dueDate }
      } catch (error) {
        if (NoObjectGeneratedError.isInstance(error)) flagForReview(error.text);
        else throw error;
      }

      Why it matters, pros and cons

      Why it matters

      Prose is loosely shaped. The schema tells the model the shape and checks the reply before your code sees it.

      Pros

      • output is typed from the schema.
      • Bad replies throw one named error, not a bad row.
      • .describe() guides each field.

      Cons

      • Valid shape is not correct content; still check values.
      • Strict schemas can be slower or unsupported on some models.
      • With tools, the output counts as a step in stopWhen.