{"record":{"id":"233c720a9543593b","repo":"vercel/ai","slug":"google-schema-conversion-does-not-support-recursiv","errorCode":null,"errorMessage":"Google schema conversion does not support recursive JSON Schema references.","messagePattern":"Google schema conversion does not support recursive JSON Schema references\\.","errorType":"exception","errorClass":"UnsupportedFunctionalityError","httpStatus":null,"severity":"error","filePath":"packages/google/src/convert-json-schema-to-openapi-schema.ts","lineNumber":219,"sourceCode":"\nfunction convertJSONSchemaReference({\n  jsonSchema,\n  reference,\n  isRoot,\n  referenceContext,\n}: {\n  jsonSchema: JSONSchema7;\n  reference: string;\n  isRoot: boolean;\n  referenceContext: ReferenceContext;\n}): unknown {\n  const { definition, referenceKey } = getReferencedDefinition(\n    reference,\n    referenceContext,\n  );\n\n  if (referenceContext.resolvingReferences.has(referenceKey)) {\n    throw new UnsupportedFunctionalityError({\n      functionality: `${recursiveReferenceFunctionalityPrefix} ${reference}`,\n      message:\n        'Google schema conversion does not support recursive JSON Schema references.',\n    });\n  }\n\n  const resolvingReferences = new Set(referenceContext.resolvingReferences);\n  resolvingReferences.add(referenceKey);\n\n  // Inline references instead of emitting Google's `ref` / `defs` fields.\n  // Those fields are supported by Vertex AI's Schema representation but are\n  // rejected by the Gemini Developer API representation used by this shared\n  // converter.\n  const { $ref: _reference, ...siblingSchema } = jsonSchema;\n  const resolvedSchema =\n    typeof definition === 'boolean'\n      ? definition\n        ? siblingSchema","sourceCodeStart":201,"sourceCodeEnd":237,"githubUrl":"https://github.com/vercel/ai/blob/69428b1f8b037e4d118fb4853428d5c4e620493c/packages/google/src/convert-json-schema-to-openapi-schema.ts#L201-L237","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","solutions":["Flatten the recursion: replace recursive definitions with a bounded depth (e.g. 3-4 explicitly nested levels) or an array-of-strings representation.","Use a provider that supports recursive schemas (e.g. OpenAI) if recursion is essential.","Serialize recursive data yourself (e.g. pass as a string field with instructions) instead of typed schema recursion."],"exampleFix":"// before\nconst nodeSchema = z.lazy(() => z.object({ name: z.string(), children: z.array(nodeSchema) }));\n// after\nconst nodeSchema = z.object({ name: z.string(), children: z.array(z.object({ name: z.string() })) }); // bounded depth","handlingStrategy":"validation","validationCode":"function hasRecursiveRef(schema, seen = new Set()) {\n  if (schema && typeof schema === 'object') {\n    if (typeof schema.$ref === 'string') {\n      const key = schema.$ref.split('/').pop();\n      if (seen.has(key)) return true;\n      seen.add(key);\n    }\n    return Object.values(schema).some(v => hasRecursiveRef(v, new Set(seen)));\n  }\n  return false;\n}\n// if (hasRecursiveRef(jsonSchema)) throw / flatten before calling Google","typeGuard":"function isNonRecursiveSchema(schema, rootDefs = schema.$defs ?? schema.definitions ?? {}) {\n  return !Object.keys(rootDefs).some(def => JSON.stringify(schema).includes(`#/$defs/${def}`) && JSON.stringify(rootDefs[def]).includes(`$defs/${def}`));\n}","tryCatchPattern":"try {\n  await generateText({ model: google('gemini-2.0-flash'), tools });\n} catch (e) {\n  if (e?.message?.includes('recursive JSON Schema references')) {\n    // fall back to flattened schema or another provider\n  } else throw e;\n}","preventionTips":["Avoid z.lazy() / self-referencing types in tool and structured-output schemas for Google.","Bound recursion depth explicitly in your domain types.","Test schema conversion in CI with the target provider before deploying."],"tags":["google","json-schema","tool-calling","schema-conversion"],"backgroundTag":"recursive-json-schema-unsupported","analyzedSha":"69428b1f8b037e4d118fb4853428d5c4e620493c","analyzedAt":"2026-08-30T12:32:21.016Z","schemaVersion":2},"datasetVersion":"2026-08-30T13:17:10.514Z"}