{"record":{"id":"3d0a08f095fc97ea","repo":"shadcn-ui/ui","slug":"missing-env-vars","errorCode":"MISSING_ENV_VARS","errorMessage":"Registry \"${registryName}\" requires the following environment variables:\n\n  • ${v}","messagePattern":"Registry \"(.+?)\" requires the following environment variables:\n\n  • (.+?)","errorType":"exception","errorClass":"RegistryMissingEnvironmentVariablesError","httpStatus":null,"severity":"error","filePath":"packages/shadcn/src/registry/validator.ts","lineNumber":48,"sourceCode":"    if (config.headers) {\n      Object.values(config.headers).forEach((value) => {\n        extractEnvVars(value).forEach((v) => vars.add(v))\n      })\n    }\n  }\n\n  return Array.from(vars)\n}\n\nexport function validateRegistryConfig(\n  registryName: string,\n  config: z.infer<typeof registryConfigItemSchema>\n): void {\n  const requiredVars = extractEnvVarsFromRegistryConfig(config)\n  const missing = requiredVars.filter((v) => !getRegistryEnvFromContext(v))\n\n  if (missing.length > 0) {\n    throw new RegistryMissingEnvironmentVariablesError(registryName, missing)\n  }\n}\n\nexport function validateRegistryConfigForItems(\n  items: string[],\n  config?: Config\n): void {\n  for (const item of items) {\n    if (isGitHubRegistrySource(item)) {\n      continue\n    }\n\n    buildUrlAndHeadersForRegistryItem(item, configWithDefaults(config))\n  }\n\n  // Clear the registry context after validation.\n  clearRegistryContext()\n}","sourceCodeStart":30,"sourceCodeEnd":66,"githubUrl":"https://github.com/shadcn-ui/ui/blob/efac5987074af84ece57c367c6dd83387b967022/packages/shadcn/src/registry/validator.ts#L30-L66","documentation":"Thrown as RegistryMissingEnvironmentVariablesError (code MISSING_ENV_VARS) by validateRegistryConfig when a registry config references environment variables via ${VAR} placeholders (in url, params, or headers) that are not available in the registry context. The context env comes from withRegistryContext or falls back to process.env.","triggerScenarios":"extractEnvVarsFromRegistryConfig scans the registry's url/params/headers strings for ${VAR} tokens; each token is checked with getRegistryEnvFromContext. Any token whose value is undefined (neither in the AsyncLocalStorage context env nor process.env) is collected, and if the missing list is non-empty the error is thrown.","commonSituations":"A private registry URL like \"https://api.example.com/r/${API_KEY}/item\" run without API_KEY set; CI that forgot to export the token; .env not loaded; typo in the variable name so it never matches process.env.","solutions":["Set each variable listed in the message in your shell, .env, or .env.local before running the command.","If running programmatically, wrap the call in withRegistryContext(callback, { env: { VAR: value } }) so the context provides the values.","Check for typos: the placeholder name must match the env key exactly (word characters only, per the ${\\w+} regex).","Remove the placeholder from the registry config if that variable is no longer needed."],"exampleFix":"// before — API_KEY missing\nconst config = { url: \"https://api.x.com/r/${API_KEY}/items\" }\n// after\nprocess.env.API_KEY = \"secret\"\n// or programmatically:\nwithRegistryContext(() => runRegistry(), { env: { API_KEY: \"secret\" } })","handlingStrategy":"validation","validationCode":"function extractEnvVars(value: string): string[] {\n  const vars: string[] = []\n  let m: RegExpExecArray | null\n  const re = /\\${(\\w+)}/g\n  while ((m = re.exec(value))) vars.push(m[1])\n  return vars\n}\nconst required = new Set<string>()\nfor (const v of Object.values(config.params ?? {})) extractEnvVars(v).forEach((x) => required.add(x))\nextractEnvVars(config.url).forEach((x) => required.add(x))\nconst missing = [...required].filter((v) => !process.env[v])\nif (missing.length) throw new Error(`missing env vars: ${missing.join(\", \")}`)","typeGuard":"const hasAllEnvVars = (config, env = process.env) => {\n  const need = new Set<string>()\n  const scan = (s: string) => { const re = /\\${(\\w+)}/g; let m; while ((m = re.exec(s))) need.add(m[1]) }\n  scan(config.url); Object.values(config.params ?? {}).forEach(scan); Object.values(config.headers ?? {}).forEach(scan)\n  return [...need].every((v) => env[v] !== undefined)\n}","tryCatchPattern":"try {\n  validateRegistryConfig(name, config)\n} catch (e) {\n  if (e.name === \"RegistryMissingEnvironmentVariablesError\") {\n    // e.missingVars lists what to set\n  }\n  throw e\n}","preventionTips":["Document required env vars next to each registry config.","Load .env before invoking shadcn (e.g. via dotenv).","Use withRegistryContext(cb, { env }) in programmatic flows so values do not leak to process.env.","Double-check placeholder spelling against the env key."],"tags":["registry","env","validation","config"],"backgroundTag":null,"analyzedSha":"efac5987074af84ece57c367c6dd83387b967022","analyzedAt":"2026-08-12T05:00:50.218Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}