vercel/ai · error · UnsupportedFunctionalityError

Google schema conversion does not support recursive JSON Sch

Error message

Google schema conversion does not support recursive JSON Schema references.

What it means

Google's OpenAPI-compatible schema format (used for tool/function calling and structured output) cannot express recursive $ref cycles. During conversion, if a JSON Schema `$ref` is encountered while that same reference is already being resolved (a cycle), the converter throws instead of emitting an invalid recursive schema.

Source

Thrown at packages/google/src/convert-json-schema-to-openapi-schema.ts:219

function convertJSONSchemaReference({
  jsonSchema,
  reference,
  isRoot,
  referenceContext,
}: {
  jsonSchema: JSONSchema7;
  reference: string;
  isRoot: boolean;
  referenceContext: ReferenceContext;
}): unknown {
  const { definition, referenceKey } = getReferencedDefinition(
    reference,
    referenceContext,
  );

  if (referenceContext.resolvingReferences.has(referenceKey)) {
    throw new UnsupportedFunctionalityError({
      functionality: `${recursiveReferenceFunctionalityPrefix} ${reference}`,
      message:
        'Google schema conversion does not support recursive JSON Schema references.',
    });
  }

  const resolvingReferences = new Set(referenceContext.resolvingReferences);
  resolvingReferences.add(referenceKey);

  // Inline references instead of emitting Google's `ref` / `defs` fields.
  // Those fields are supported by Vertex AI's Schema representation but are
  // rejected by the Gemini Developer API representation used by this shared
  // converter.
  const { $ref: _reference, ...siblingSchema } = jsonSchema;
  const resolvedSchema =
    typeof definition === 'boolean'
      ? definition
        ? siblingSchema

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Flatten the recursion: replace recursive definitions with a bounded depth (e.g. 3-4 explicitly nested levels) or an array-of-strings representation.
  2. Use a provider that supports recursive schemas (e.g. OpenAI) if recursion is essential.
  3. Serialize recursive data yourself (e.g. pass as a string field with instructions) instead of typed schema recursion.

Example fix

// before
const nodeSchema = z.lazy(() => z.object({ name: z.string(), children: z.array(nodeSchema) }));
// after
const nodeSchema = z.object({ name: z.string(), children: z.array(z.object({ name: z.string() })) }); // bounded depth
Defensive patterns

Strategy: validation

Validate before calling

function hasRecursiveRef(schema, seen = new Set()) {
  if (schema && typeof schema === 'object') {
    if (typeof schema.$ref === 'string') {
      const key = schema.$ref.split('/').pop();
      if (seen.has(key)) return true;
      seen.add(key);
    }
    return Object.values(schema).some(v => hasRecursiveRef(v, new Set(seen)));
  }
  return false;
}
// if (hasRecursiveRef(jsonSchema)) throw / flatten before calling Google

Type guard

function isNonRecursiveSchema(schema, rootDefs = schema.$defs ?? schema.definitions ?? {}) {
  return !Object.keys(rootDefs).some(def => JSON.stringify(schema).includes(`#/$defs/${def}`) && JSON.stringify(rootDefs[def]).includes(`$defs/${def}`));
}

Try / catch

try {
  await generateText({ model: google('gemini-2.0-flash'), tools });
} catch (e) {
  if (e?.message?.includes('recursive JSON Schema references')) {
    // fall back to flattened schema or another provider
  } else throw e;
}

Prevention

When it happens

Trigger: Passing a zod schema converted via z.toJSONSchema (or a hand-written JSON Schema) that contains a self-referencing definition, e.g. a `Node` type with `children: Node[]`, into `generateText`/`generateObject` with a Google model. Detected by the `resolvingReferences` set in convertJSONSchemaReference.

Common situations: Tree/graph-shaped tool inputs (JSON, org charts, ASTs); recursive zod schemas using z.lazy(); migrating tool schemas from OpenAI (which supports recursion) to Google.

Related errors


AI-assisted analysis of vercel/ai@69428b1f8b (2026-08-30). Data as JSON: /api/errors/233c720a9543593b. Report an issue: GitHub.