CherryHQ/cherry-studio · error · Error

[theme-contract] ${label} contains duplicate names

Error message

[theme-contract] ${label} contains duplicate names

What it means

Thrown by the theme contract validator when a labeled list of token names contains duplicates. The assertUnique function checks that new Set(values).size === values.length for various token arrays (runtime theme inputs, Shadcn variables, product variables, Tailwind product colors, compatibility tokens). A duplicate means the same variable name appears twice in a source-of-truth token list, which would cause ambiguous resolution.

Source

Thrown at packages/ui/scripts/validate-theme-contract.ts:108

  })
}

function extractImports(source: string): string[] {
  return [...stripComments(source).matchAll(/@import\s+([^;]+);/g)].map((match) => {
    const importValue = match[1].trim()
    const stringMatch = importValue.match(/^(['"])([^'"]+)\1$/)
    if (stringMatch) return stringMatch[2]

    const urlMatch = importValue.match(/^url\(\s*(?:(['"])([^'"]+)\1|([^'")\s][^)]*?))\s*\)$/)
    if (urlMatch) return (urlMatch[2] ?? urlMatch[3]).trim()

    throw new Error(`[theme-contract] unsupported @import syntax: ${importValue}`)
  })
}

function assertUnique(label: string, values: readonly string[]): void {
  if (new Set(values).size !== values.length) {
    throw new Error(`[theme-contract] ${label} contains duplicate names`)
  }
}

function assertSurfacePairs(
  label: string,
  pairs: ReadonlyArray<readonly [surface: string, foreground: string]>,
  variableNames: Set<string>
): void {
  const surfaces = new Set<string>()

  for (const [surface, foreground] of pairs) {
    if (surface === foreground || surfaces.has(surface)) {
      throw new Error(`[theme-contract] ${label} has an invalid or duplicate surface pair for ${surface}`)
    }
    if (!variableNames.has(surface) || !variableNames.has(foreground)) {
      throw new Error(`[theme-contract] ${label} pair ${surface} / ${foreground} is outside its public contract`)
    }
    surfaces.add(surface)

View on GitHub (pinned to 726446b54c)

Solutions

  1. Inspect the token constant array identified by the label in the error message (e.g., 'Shadcn variables', 'product variables') in packages/ui/scripts/theme-contract.ts.
  2. Remove the duplicate entry so each name appears exactly once.
  3. Run the validator to confirm: npx tsx packages/ui/scripts/validate-theme-contract.ts.

Example fix

// before — packages/ui/scripts/theme-contract.ts
export const SHADCN_VARIABLE_TOKENS = [
  'background',
  'foreground',
  'background',  // ← duplicate
  'card'
] as const

// after — remove duplicate
export const SHADCN_VARIABLE_TOKENS = [
  'background',
  'foreground',
  'card'
] as const
Defensive patterns

Strategy: validation

Validate before calling

// Check token arrays for duplicates before committing theme-contract.ts changes
import { SHADCN_VARIABLE_TOKENS, CHERRY_PRODUCT_VARIABLE_TOKENS, RUNTIME_THEME_INPUT_TOKENS } from './theme-contract'

function assertUnique(label: string, values: readonly string[]): void {
  if (new Set(values).size !== values.length) {
    const dupes = values.filter((v, i) => values.indexOf(v) !== i)
    throw new Error(`${label} has duplicates: ${dupes.join(', ')}`)
  }
}

assertUnique('Shadcn variables', SHADCN_VARIABLE_TOKENS)
assertUnique('product variables', CHERRY_PRODUCT_VARIABLE_TOKENS)
assertUnique('runtime inputs', RUNTIME_THEME_INPUT_TOKENS)

Prevention

When it happens

Trigger: A token constant array in packages/ui/scripts/theme-contract.ts (e.g., SHADCN_VARIABLE_TOKENS, CHERRY_PRODUCT_VARIABLE_TOKENS, RUNTIME_THEME_INPUT_TOKENS, or one of the COMPATIBILITY_*_TOKENS arrays) contains the same string twice. The validator calls assertUnique on each array at the start of validateThemeContractSources.

Common situations: A developer adds a new token to a constant array but it was already present (e.g., adding 'background' when it's already listed). Copy-paste error when extending the token lists. A merge that combined two branches each adding the same token. Refactoring that moved tokens between arrays without removing the original.

Related errors


AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12). Data as JSON: /api/errors/2cf7e3047666a9d5. Report an issue: GitHub.