{"record":{"id":"5638161c9f200fb1","repo":"shadcn-ui/ui","slug":"invalid-input-not-an-object-literal","errorCode":null,"errorMessage":"Invalid input: not an object literal","messagePattern":"Invalid input: not an object literal","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/shadcn/src/utils/updaters/update-tailwind-config.ts","lineNumber":433,"sourceCode":"\nasync function parseObjectLiteral(objectLiteralString: string): Promise<any> {\n  const sourceFile = await _createSourceFile(\n    `const theme = ${objectLiteralString}`,\n    null\n  )\n\n  const statement = sourceFile.getStatements()[0]\n  if (statement?.getKind() === SyntaxKind.VariableStatement) {\n    const declaration = (statement as VariableStatement)\n      .getDeclarationList()\n      ?.getDeclarations()[0]\n    const initializer = declaration.getInitializer()\n    if (initializer?.isKind(SyntaxKind.ObjectLiteralExpression)) {\n      return await parseObjectLiteralExpression(initializer)\n    }\n  }\n\n  throw new Error(\"Invalid input: not an object literal\")\n}\n\nfunction parseObjectLiteralExpression(node: ObjectLiteralExpression): any {\n  const result: any = {}\n  for (const property of node.getProperties()) {\n    if (property.isKind(SyntaxKind.PropertyAssignment)) {\n      const name = property.getName().replace(/\\'/g, \"\")\n      if (\n        property.getInitializer()?.isKind(SyntaxKind.ObjectLiteralExpression)\n      ) {\n        result[name] = parseObjectLiteralExpression(\n          property.getInitializer() as ObjectLiteralExpression\n        )\n      } else if (\n        property.getInitializer()?.isKind(SyntaxKind.ArrayLiteralExpression)\n      ) {\n        result[name] = parseArrayLiteralExpression(\n          property.getInitializer() as ArrayLiteralExpression","sourceCodeStart":415,"sourceCodeEnd":451,"githubUrl":"https://github.com/shadcn-ui/ui/blob/efac5987074af84ece57c367c6dd83387b967022/packages/shadcn/src/utils/updaters/update-tailwind-config.ts#L415-L451","documentation":"Thrown by shadcn's tailwind-config updater (ts-morph based) when it cannot find an object literal to mutate. The updater grabs sourceFile.getStatements()[0], expects a VariableStatement whose first declaration's initializer is an ObjectLiteralExpression, and throws if either check fails. It is a hard precondition: shadcn can only patch a config that is a plain exported object literal.","triggerScenarios":"Calling `shadcn` (init/add) against a tailwind.config.{js,ts,mjs,cjs} whose first statement is NOT `const config = { ... }` / `export default { ... }` / `module.exports = { ... }`. Triggered when the config exports a function (e.g. `export default function() { return {...} }`), a conditional/ternary, a Promise, a merge like `module.exports = merge(...)`, an empty file, or a file whose first statement is an import/pragma rather than the config variable.","commonSituations":"Tailwind v4 setups that use a `@theme` CSS-first config and leave the JS config minimal or function-shaped; configs wrapped in `config({ ... })` helpers (e.g. from `@tailwindcss/typography` or custom wrappers); configs generated by other tools that export a function; monorepos where shadcn picks the wrong config path.","solutions":["Rewrite the config so its first top-level statement is a variable/export holding a literal object: `const config = { content: [...], theme: {...} }; export default config` (or `export default { ... }` / `module.exports = { ... }`). No function wrappers, no ternaries, no merge() calls around the object.","Remove any `import`/comment-only or directive first lines so getStatements()[0] is the config variable statement, and move imports above it only if they remain statements before it that are NOT the config (ts-morph indexes the literal first statement, so the config must be statement [0]).","If you need a merged/derived config, build it as a plain object literal first and only assign/merge INTO that literal, then export the literal; do not export the result of a helper call.","Pin/upgrade shadcn to a version matching your Tailwind major (v3 vs v4) so the updater targets the correct file shape."],"exampleFix":"// before\nconst config = merge(baseConfig, {\n  theme: { extend: { colors: {} } }\n})\nexport default config\n\n// after\nconst config = {\n  ...baseConfig,\n  theme: { extend: { colors: {} } }\n}\nexport default config","handlingStrategy":"validation","validationCode":"// Before calling the shadcn updater, sanity-check the config shape with ts-morph:\nimport { Project, SyntaxKind } from \"ts-morph\"\nconst sf = new Project().addSourceFileAtPath(\"tailwind.config.ts\")\nconst stmt = sf.getStatements()[0]\nconst decl = stmt?.getKind() === SyntaxKind.VariableStatement\n  ? stmt.getFirstChildByKind(SyntaxKind.VariableDeclarationList)\n      ?.getDeclarations()[0]\n  : undefined\nconst ok = decl?.getInitializer()?.isKind(SyntaxKind.ObjectLiteralExpression) ?? false\nif (!ok) throw new Error(\"tailwind config is not a plain object literal; shadcn cannot patch it\")","typeGuard":"// Narrow an exported config node to a patchable object literal.\nimport { Node, ObjectLiteralExpression, SyntaxKind } from \"ts-morph\"\nfunction isPatchableObjectLiteral(node: Node | undefined): node is ObjectLiteralExpression {\n  return node?.isKind(SyntaxKind.ObjectLiteralExpression) ?? false\n}","tryCatchPattern":"try {\n  await updateTailwindConfig(...) // shadcn call\n} catch (e) {\n  if (String(e?.message).includes(\"not an object literal\")) {\n    throw new Error(\"tailwind.config must export a plain object literal; rewrite merge()/function wrappers before re-running shadcn.\")\n  }\n  throw e\n}","preventionTips":["Keep tailwind.config as a single exported object literal — no function bodies, no helper-wrapped exports.","Avoid `module.exports = merge(...)` or `config({...})` wrappers around the exported config.","Run `shadcn init` against a freshly generated Tailwind config so the shape matches the updater's expectation.","For Tailwind v4 (CSS-first), confirm shadcn version supports the v4 config model before running add/init."],"tags":["shadcn","tailwind","ts-morph","config","build-tooling"],"backgroundTag":null,"analyzedSha":"efac5987074af84ece57c367c6dd83387b967022","analyzedAt":"2026-08-12T05:00:50.218Z","schemaVersion":2},"datasetVersion":"2026-08-12T13:17:24.610Z"}