{"record":{"id":"914c1519e5ec3e1a","repo":"shadcn-ui/ui","slug":"validation-error-914c15","errorCode":"VALIDATION_ERROR","errorMessage":"Invalid registry file at ${rootFile}: registries that use include must be named registry.json.","messagePattern":"Invalid registry file at (.+?): registries that use include must be named registry\\.json\\.","errorType":"validation","errorClass":"RegistryValidationError","httpStatus":null,"severity":"error","filePath":"packages/shadcn/src/registry/loader.ts","lineNumber":103,"sourceCode":"      const source = {\n        registryFile: rootFile,\n        registryDir: context.cwd,\n        itemIndex,\n      }\n      context.itemSources.set(item.name, source)\n      context.itemSourcesByItem.set(item, source)\n    })\n\n    return {\n      registry: rootRegistry,\n      itemSources: context.itemSources,\n      itemSourcesByItem: context.itemSourcesByItem,\n      usesInclude,\n    }\n  }\n\n  if (path.basename(rootFile) !== \"registry.json\") {\n    throw new RegistryValidationError(\n      `Invalid registry file at ${rootFile}: registries that use include must be named registry.json.`,\n      { registryFile: rootFile }\n    )\n  }\n\n  const result = await readRegistryFile(rootFile, rootRegistry, context, [])\n\n  validateDuplicateItems(result.items, context.itemSourcesByItem)\n\n  const { include, ...registry } = result\n  validateRootRegistry(registry, rootFile)\n\n  return {\n    registry,\n    itemSources: context.itemSources,\n    itemSourcesByItem: context.itemSourcesByItem,\n    usesInclude,\n  }","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/shadcn-ui/ui/blob/efac5987074af84ece57c367c6dd83387b967022/packages/shadcn/src/registry/loader.ts#L85-L121","documentation":"Thrown by readRegistryWithIncludes when a registry file declares an 'include' array but is not itself named registry.json. shadcn enforces a naming convention: only a file literally named registry.json may act as the root of an include tree, because the include resolution logic and downstream path rewriting anchor on that basename. Without it, included chunks cannot be reliably resolved relative to a stable root.","triggerScenarios":"Called loadRegistry({ registryFile }) or loadRegistryItem(name, { registryFile }) where the resolved root file contains an 'include' field (usesInclude === true) AND path.basename(rootFile) !== 'registry.json'. E.g. registryFile: 'my-registry.json' with {\"include\": [\"./ui/registry.json\"]}.","commonSituations":"Renaming the root registry file from registry.json to something project-specific (e.g. shadcn-registry.json, app-registry.json) while keeping the include field. Copying a registry config from a sub-package to the repo root without renaming. Migrating from a flat registry to a split one and forgetting the rename.","solutions":["Rename the root registry file to registry.json (the literal basename must be 'registry.json').","If you cannot rename the file, remove the 'include' field from it and instead point loadRegistry directly at the appropriate registry.json.","Pass an explicit registryFile option that resolves to a path whose basename is registry.json."],"exampleFix":"// before\n// file: ./app-registry.json  with  { \"include\": [\"./ui/registry.json\"] }\nawait loadRegistry({ registryFile: \"app-registry.json\" })\n\n// after\n// rename file to ./registry.json\nawait loadRegistry({ registryFile: \"registry.json\" })","handlingStrategy":"validation","validationCode":"import * as path from \"path\"\n\nfunction assertRootRegistryName(registryFile: string, hasIncludes: boolean) {\n  if (hasIncludes && path.basename(registryFile) !== \"registry.json\") {\n    throw new Error(\n      `Root registry '${registryFile}' uses includes but is not named registry.json.`\n    )\n  }\n}\n\n// before calling loadRegistry:\nconst raw = JSON.parse(await fs.readFile(registryFile, \"utf-8\"))\nassertRootRegistryName(registryFile, Array.isArray(raw.include) && raw.include.length > 0)","typeGuard":"function isRootRegistryCandidate(file: string, content: unknown): boolean {\n  if (path.basename(file) !== \"registry.json\") return false\n  if (!content || typeof content !== \"object\") return false\n  const includes = (content as { include?: unknown }).include\n  return !Array.isArray(includes) || includes.length === 0 || path.basename(file) === \"registry.json\"\n}","tryCatchPattern":"try {\n  await loadRegistry({ registryFile })\n} catch (err) {\n  if (err instanceof RegistryError && err.code === \"VALIDATION_ERROR\" && /must be named registry\\.json/.test(err.message)) {\n    // rename the file or drop the include field\n  }\n  throw err\n}","preventionTips":["Keep the root registry file named literally registry.json.","Run a CI lint that asserts the root registry basename before build.","Document the naming convention in your registry README."],"tags":["registry","validation","include","naming-convention"],"backgroundTag":null,"analyzedSha":"efac5987074af84ece57c367c6dd83387b967022","analyzedAt":"2026-08-12T05:00:50.218Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}