Skip to content

[bug]: Structured output fails on optional Zod fields in native JSON-schema mode #335

Description

@EricLiu2795

Summary

Context

While validating the Founder Weekly Review quality follow-up in #333, the realistic E2E reached generation and failed during structured-output schema preparation before any provider request was sent.

The failure is caused by an existing optional field in the Founder Weekly Review schema:

rationale: z.string().min(1).max(2000).optional()

The problem appears at the shared structured-output boundary when the configured model declares native JSON-schema support.

Current behavior

Founder Weekly Review generation uses the shared invokeStructured(...) path through the configured reasoning route.

The current flow is approximately:

Zod schema
→ invokeStructured(...)
→ model declares native structured output
→ native json-schema mode selected
→ LangChain withStructuredOutput(...)
→ schema preparation
→ failure on optional Zod field

The failure occurs before the model/provider request is made.

In the observed realistic E2E:

computed evidence collection
→ Snapshot v2 validation
→ document-change audit/group validation
→ isolated DB setup
→ generation start
→ structured-output schema preparation
→ failure

No generation provider call was completed.

Why this was not previously visible

The historical Founder Weekly Review implementation used a different structured-output path for Kimi/Moonshot.

That path used JSON-object generation followed by local Zod validation:

Zod schema
→ schema guidance in prompt
→ provider returns JSON object
→ local schema.parse(...)

With that approach, an omitted field such as rationale is valid when the Zod schema marks it as .optional().

The current shared architecture instead prefers native JSON-schema when the configured model advertises that capability, exposing a compatibility mismatch between otherwise-valid Zod schemas and the native structured-output path.

Reproduction

A minimal reproduction should use the shared structured-output adapter with a schema containing an optional field, for example:

const ExampleSchema = z.object({
required: z.string(),
optional: z.string().optional(),
});

Then invoke it through a configured model whose behavior advertises native json-schema.

Expected behavior

The schema is accepted and structured generation proceeds.

Actual behavior

Structured-output schema preparation fails before the provider request.

@launchstack/core version (if applicable)

No response

Node.js version

No response

Affected area

@launchstack/features

Additional context

No response

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions