Skip to content

Type conflict: @ag-ui/core 0.0.49 (Zod 3) breaks consumer Zod 4 type checking #520

@stevan-borus

Description

@stevan-borus

Problem

@tanstack/ai itself is on Zod 4 (^4.2.0), but pins @ag-ui/core@0.0.49 which still depends on zod: ^3.22.4. This pulls Zod 3 into the consumer's type graph as a transitive dep, even when the consumer's app is fully on Zod 4.

This breaks consumers using @hookform/resolvers/zod v5 (or any other library with Zod-3-vs-4 dual overload typings), because TypeScript's overload resolver compares two different zod/v4/core/versions.d.ts files in the same compilation:

  • Zod 3.25.x's v4/core shim: version.minor: 0
  • Zod 4.x: version.minor: 3 (or current)

Producing errors like:

error TS2769: No overload matches this call.
  Overload 2 of 4, '(schema: \$ZodType<...>, ...): Resolver<...>', gave the following error.
    The types of '_zod.version.minor' are incompatible between these types.
      Type '3' is not assignable to type '0'.

Repro

  1. Install @tanstack/ai (any recent version)
  2. Install @hookform/resolvers@5 and zod@^4.3
  3. Run tsc --noEmit on any file calling zodResolver(z.object({...}))

Tracking upstream

The right long-term fix is @ag-ui/core moving to Zod 4 — tracked in ag-ui-protocol/ag-ui#1397 (open, no ETA).

Suggested workarounds for @tanstack/ai

While #1397 is unresolved:

  1. Document the transitive Zod 3 footgun in the @tanstack/ai README / migration notes so consumers know to expect resolver typing breakage.
  2. Vendor or shim @ag-ui/core types internally so zod/v3 doesn't surface in consumer type graphs.
  3. Or just push #1397 to land — @tanstack/ai is the largest downstream consumer of @ag-ui/core and your influence there would help.

Happy to PR #1 if useful.

Local workaround we used

Module declaration override in our app:

// types/hookform-resolvers.d.ts
declare module '@hookform/resolvers/zod' {
  import type { FieldValues, Resolver } from 'react-hook-form';
  export function zodResolver<TInput extends FieldValues, TContext = unknown, TOutput = TInput>(
    schema: { _zod: { output: TOutput; input: TInput } } | { _output: TOutput; _input: TInput; _def: { typeName: string } },
    schemaOptions?: unknown,
    resolverOptions?: { mode?: 'async' | 'sync'; raw?: false },
  ): Resolver<TInput, TContext, TOutput>;
}

Runtime is fine — both Zod 3 and Zod 4 schemas are recognized at runtime by the resolver.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions