vercel/ai · error · UnsupportedFunctionalityError

Google schema conversion only supports references to direct

Error message

Google schema conversion only supports references to direct children of root-level $defs or definitions.

What it means

The Google schema converter only resolves `$ref` pointers that target a definition directly under the root-level `$defs` (or `definitions`) object, e.g. `#/$defs/Foo`. Any other reference form (nested paths, anchors, external refs, or refs to sub-properties) cannot be mapped to Google's flat OpenAPI schema and throws UnsupportedFunctionalityError via throwUnsupportedReference.

Source

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

  const definitionName = decodedDefinitionName.replace(/~[01]/g, match =>
    match === '~1' ? '/' : '~',
  );

  if (
    !Object.prototype.hasOwnProperty.call(source.definitions, definitionName)
  ) {
    throwUnsupportedReference(reference);
  }

  return {
    definition: source.definitions[definitionName],
    referenceKey: `${source.prefix}${definitionName}`,
  };
}

function throwUnsupportedReference(reference: string): never {
  throw new UnsupportedFunctionalityError({
    functionality: `JSON Schema reference: ${reference}`,
    message:
      'Google schema conversion only supports references to direct children of root-level $defs or definitions.',
  });
}

type EnumValues = NonNullable<JSONSchema7['enum']>;
type EnumType = 'string' | 'number' | 'integer' | 'boolean';
type GoogleEnumSchema = {
  type?: JSONSchema7['type'];
  enum?: JSONSchema7['enum'];
  format?: JSONSchema7['format'];
  anyOf?: JSONSchema7['anyOf'];
  nullable?: boolean;
};

function addEnumToSchema({
  values,

View on GitHub (pinned to 69428b1f8b)

Solutions

  1. Hoist every referenced type to the top-level `$defs`/`definitions` object and reference it as `#/$defs/Name`.
  2. Inline the referenced sub-schema directly where the ref occurs.
  3. Run a pre-processing step (e.g. with a $ref bundler like @apidevtools/json-schema-ref-parser) to normalize refs before passing to the model.

Example fix

// before
{ "type": "object", "properties": { "bar": { "$ref": "#/$defs/Foo/properties/bar" } }, "$defs": { "Foo": { "properties": { "bar": { "type": "string" } } } } }
// after
{ "type": "object", "properties": { "bar": { "$ref": "#/$defs/Bar" } }, "$defs": { "Bar": { "type": "string" } } }
Defensive patterns

Strategy: validation

Validate before calling

function refsAreDirectChildren(schema) {
  const allowed = new Set(Object.keys(schema?.$defs ?? schema?.definitions ?? {}).map(k => `#/$defs/${k}`));
  const refs = JSON.stringify(schema).match(/"\$ref"\s*:\s*"([^"]+)"/g) ?? [];
  return refs.every(r => { const v = JSON.parse(r.replace(/^"\$ref"\s*:\s*/, '')); return allowed.has(v); });
}

Type guard

function hasOnlyRootLevelRefs(schema) {
  const keys = new Set(Object.keys(schema.$defs ?? {}));
  const walk = (node) => node === null || typeof node !== 'object' ? true
    : (typeof node.$ref === 'string' ? /^#\/\$defs\/[\w-]+$/.test(node.$ref) && keys.has(node.$ref.split('/')[2]) : Object.values(node).every(walk));
  return walk(schema);
}

Try / catch

try {
  await generateObject({ model: googleModel, schema });
} catch (e) {
  if (e?.message?.includes('direct children of root-level $defs')) {
    // normalize refs (inline or hoist) and retry
  } else throw e;
}

Prevention

When it happens

Trigger: A JSON Schema with `$ref: '#/$defs/Foo/properties/bar'`, `$ref: '#/definitions/A/B'`, relative or external `$ref`s, or definitions nested inside other definitions, passed to a Google model for tool/structured output conversion.

Common situations: Hand-edited JSON Schemas with nested refs; schemas generated by tools that emit deep pointer refs; schemas ported from OpenAPI documents with component-internal references.

Related errors


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