{"id":"fa5bef923e27c0c9","repo":"colinhacks/zod","slug":"external-ref-is-not-supported-only-local-refs","errorCode":null,"errorMessage":"External $ref is not supported, only local refs (#/...) are allowed","messagePattern":"External \\$ref is not supported, only local refs \\(#/\\.\\.\\.\\) are allowed","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/zod/src/v4/classic/from-json-schema.ts","lineNumber":123,"sourceCode":"  const $schema = schema.$schema;\n\n  if ($schema === \"https://json-schema.org/draft/2020-12/schema\") {\n    return \"draft-2020-12\";\n  }\n  if ($schema === \"http://json-schema.org/draft-07/schema#\") {\n    return \"draft-7\";\n  }\n  if ($schema === \"http://json-schema.org/draft-04/schema#\") {\n    return \"draft-4\";\n  }\n\n  // Use defaultTarget if provided, otherwise default to draft-2020-12\n  return defaultTarget ?? \"draft-2020-12\";\n}\n\nfunction resolveRef(ref: string, ctx: ConversionContext): JSONSchema.JSONSchema {\n  if (!ref.startsWith(\"#\")) {\n    throw new Error(\"External $ref is not supported, only local refs (#/...) are allowed\");\n  }\n\n  const path = ref.slice(1).split(\"/\").filter(Boolean);\n\n  // Handle root reference \"#\"\n  if (path.length === 0) {\n    return ctx.rootSchema;\n  }\n\n  const defsKey = ctx.version === \"draft-2020-12\" ? \"$defs\" : \"definitions\";\n\n  if (path[0] === defsKey) {\n    const key = path[1];\n    if (!key || !ctx.defs[key]) {\n      throw new Error(`Reference not found: ${ref}`);\n    }\n    return ctx.defs[key]!;\n  }","sourceCodeStart":105,"sourceCodeEnd":141,"githubUrl":"https://github.com/colinhacks/zod/blob/912f0f51b0ced654d0069741e7160834dca742ee/packages/zod/src/v4/classic/from-json-schema.ts#L105-L141","documentation":"Thrown by resolveRef in the v4 JSON-Schema-to-Zod converter when a `$ref` does not start with `#`. Only local (same-document) refs are supported; the converter has no mechanism to fetch or embed external schema documents.","triggerScenarios":"Passing a JSON Schema whose `$ref` points at another file or URL, e.g. `{ \"$ref\": \"./user.json\" }`, `{ \"$ref\": \"https://example.com/schemas/user\" }`, or `{ \"$ref\": \"defs.json#/User\" }`.","commonSituations":"Loading OpenAPI/JSON Schema bundles that split definitions across files; consuming third-party schemas that reference a shared registry by URL; bundlers that do not inline external refs.","solutions":["Inline external definitions into a single `$defs` (draft 2020-12) or `definitions` (draft-07) block and rewrite refs to `#/$defs/Name`.","Use a JSON Schema bundler (e.g. @apidevtools/json-schema-ref-parser) to resolve external refs before passing to fromJSONSchema.","Drop or replace external refs with locally-defined equivalents if the external schema is small."],"exampleFix":"// before\n{ \"$ref\": \"https://example.com/user.json\" }\n\n// after\n{\n  \"$defs\": { \"user\": { \"type\": \"object\", ... } },\n  \"$ref\": \"#/$defs/user\"\n}","handlingStrategy":"validation","validationCode":"function assertLocalRefs(root: any, path = \"\") {\n  const visit = (node: any) => {\n    if (!node || typeof node !== \"object\") return;\n    if (typeof node.$ref === \"string\" && !node.$ref.startsWith(\"#\")) {\n      throw new Error(`External ref at ${path}: ${node.$ref}`);\n    }\n    for (const k of Object.keys(node)) visit(node[k]);\n  };\n  visit(root);\n}","typeGuard":"function isLocalRef(ref: string): boolean {\n  return typeof ref === \"string\" && ref.startsWith(\"#\");\n}","tryCatchPattern":null,"preventionTips":["Run a JSON Schema bundler before conversion to inline external refs.","Keep schemas self-contained in a single document with `$defs`.","Add a CI check that rejects schemas containing external `$ref` values."],"tags":["json-schema","ref","v4","from-json-schema"],"analyzedSha":"912f0f51b0ced654d0069741e7160834dca742ee","analyzedAt":"2026-08-03T17:41:55.908Z","schemaVersion":2}