colinhacks/zod · error · Error

External $ref is not supported, only local refs (#/...) are…

Error message

External $ref is not supported, only local refs (#/...) are allowed

What it means

Thrown by resolveRef in the v4 fromJSONSchema converter (packages/zod/src/v4/classic/from-json-schema.ts:124) when a schema's $ref does not begin with '#'. The converter only understands local JSON-pointer references (e.g. #/$defs/Foo) because Zod schemas live in-process; any external URL/file reference has no resolvable target and is rejected up front.

Solutions

  1. Pre-process the schema with a bundler/dereferencer (e.g. @apidevools/json-schema-ref-parser, swagger-cli bundle) so all $refs become local #/ pointers before calling fromJSONSchema.
  2. Inline external schemas manually into $defs (draft-2020-12) or definitions (draft-7) and rewrite their $refs to #/$defs/Name.
  3. Drop or replace external $refs that have no local equivalent with a concrete inline schema.
  4. Verify each $ref in the document starts with '#'; log any that don't before conversion.

Example fix

// before
const schema = {
  type: 'object',
  properties: { user: { $ref: 'https://example.com/user.json' } },
};
fromJSONSchema(schema); // throws: External $ref is not supported

// after — inline the external schema locally
const schema = {
  $defs: { User: { type: 'object', properties: { id: { type: 'string' } } } },
  type: 'object',
  properties: { user: { $ref: '#/$defs/User' } },
};
fromJSONSchema(schema);
Defensive patterns

Strategy: validation

Validate before calling

function assertAllRefsLocal(root: unknown): string[] {
  const external: string[] = [];
  const visit = (node: unknown) => {
    if (Array.isArray(node)) return node.forEach(visit);
    if (!node || typeof node !== 'object') return;
    const o = node as Record<string, unknown>;
    if (typeof o.$ref === 'string' && !o.$ref.startsWith('#')) external.push(o.$ref);
    for (const v of Object.values(o)) visit(v);
  };
  visit(root);
  return external;
}

// usage:
// const bad = assertAllRefsLocal(schema);
// if (bad.length) throw new Error(`External $refs found: ${bad.join(', ')}`);

Type guard

function isLocalRef(ref: unknown): ref is `#${string}` {
  return typeof ref === 'string' && ref.startsWith('#');
}

Try / catch

null

Prevention

When it happens

Trigger: Calling fromJSONSchema on a JSON Schema document whose $ref values point to external resources, e.g. `{ "$ref": "https://example.com/schemas/user.json" }` or `{ "$ref": "user.json" }` or `{ "$ref": "urn:uuid:..." }` — anything that is not a #-prefixed local pointer.

Common situations: Consuming OpenAPI/Swagger specs that split schemas across files via $ref; importing a multi-file JSON Schema bundle without first inlining/compiling it; tooling (e.g. @apidevtools/swagger-parser) not run to dereference; drafts using $id-based cross-document refs.

Related errors


AI-assisted analysis of colinhacks/zod@2d90846af9 (2026-08-11). Data as JSON: /api/errors/fa5bef923e27c0c9. Report an issue: GitHub.

Appendix: source

Thrown at packages/zod/src/v4/classic/from-json-schema.ts:124

  const $schema = schema.$schema;

  if ($schema === "https://json-schema.org/draft/2020-12/schema") {
    return "draft-2020-12";
  }
  if ($schema === "http://json-schema.org/draft-07/schema#") {
    return "draft-7";
  }
  if ($schema === "http://json-schema.org/draft-04/schema#") {
    return "draft-4";
  }

  // Use defaultTarget if provided, otherwise default to draft-2020-12
  return defaultTarget ?? "draft-2020-12";
}

function resolveRef(ref: string, ctx: ConversionContext): JSONSchema.JSONSchema {
  if (!ref.startsWith("#")) {
    throw new Error("External $ref is not supported, only local refs (#/...) are allowed");
  }

  const path = ref.slice(1).split("/").filter(Boolean);

  // Handle root reference "#"
  if (path.length === 0) {
    return ctx.rootSchema;
  }

  const defsKey = ctx.version === "draft-2020-12" ? "$defs" : "definitions";

  if (path[0] === defsKey) {
    const key = path[1];
    if (!key || !ctx.defs[key]) {
      throw new Error(`Reference not found: ${ref}`);
    }
    return ctx.defs[key]!;
  }

View on GitHub (pinned to 2d90846af9)