{"record":{"id":"4696263c8612a783","repo":"CherryHQ/cherry-studio","slug":"theme-contract-label-has-an-invalid-or-duplic","errorCode":null,"errorMessage":"[theme-contract] ${label} has an invalid or duplicate surface pair for ${surface}","messagePattern":"\\[theme-contract\\] (.+?) has an invalid or duplicate surface pair for (.+?)","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/ui/scripts/validate-theme-contract.ts","lineNumber":121,"sourceCode":"  })\n}\n\nfunction assertUnique(label: string, values: readonly string[]): void {\n  if (new Set(values).size !== values.length) {\n    throw new Error(`[theme-contract] ${label} contains duplicate names`)\n  }\n}\n\nfunction assertSurfacePairs(\n  label: string,\n  pairs: ReadonlyArray<readonly [surface: string, foreground: string]>,\n  variableNames: Set<string>\n): void {\n  const surfaces = new Set<string>()\n\n  for (const [surface, foreground] of pairs) {\n    if (surface === foreground || surfaces.has(surface)) {\n      throw new Error(`[theme-contract] ${label} has an invalid or duplicate surface pair for ${surface}`)\n    }\n    if (!variableNames.has(surface) || !variableNames.has(foreground)) {\n      throw new Error(`[theme-contract] ${label} pair ${surface} / ${foreground} is outside its public contract`)\n    }\n    surfaces.add(surface)\n  }\n}\n\nfunction assertExactImports(label: string, source: string, expected: readonly string[]): void {\n  const actual = extractImports(source)\n\n  if (actual.length !== expected.length || actual.some((entry, index) => entry !== expected[index])) {\n    throw new Error(`[theme-contract] ${label} imports must be exactly: ${expected.join(' -> ')}`)\n  }\n}\n\nfunction buildDeclarationMap(entries: SourceEntry[], selector: ':root' | '.dark'): Map<string, Declaration> {\n  const declarations = new Map<string, Declaration>()","sourceCodeStart":103,"sourceCodeEnd":139,"githubUrl":"https://github.com/CherryHQ/cherry-studio/blob/726446b54cd69ffe51a276638672f6d95ca0768c/packages/ui/scripts/validate-theme-contract.ts#L103-L139","documentation":"Thrown by the theme contract validator when a surface-pair list (SHADCN_SURFACE_PAIRS or CHERRY_PRODUCT_SURFACE_PAIRS) has an invalid entry: either the surface and its foreground are the same token (surface === foreground), or the same surface token appears in more than one pair. Surface pairs define matched background/foreground combos (e.g., ['card', 'card-foreground']) and each surface must be unique and distinct from its foreground.","triggerScenarios":"A surface pair array contains a self-referential pair like ['card', 'card'] (surface equals foreground), or the same surface name appears in two different pairs (e.g., ['card', 'card-foreground'] and ['card', 'card-muted'] both list 'card'). The assertSurfacePairs function iterates all pairs and tracks seen surfaces in a Set.","commonSituations":"A developer adds a new surface pair but reuses an existing surface name. A copy-paste error duplicates a pair entry. A typo where the foreground was set to the same value as the surface. A refactoring that merged or split pairs incorrectly.","solutions":["Inspect the surface pair array identified by the label (e.g., 'Shadcn contract', 'product contract') in packages/ui/scripts/theme-contract.ts.","If surface === foreground, correct the foreground to the intended distinct token.","If a surface is duplicated across pairs, remove or rename the duplicate so each surface appears exactly once.","Run the validator to confirm: npx tsx packages/ui/scripts/validate-theme-contract.ts."],"exampleFix":"// before\nexport const SHADCN_SURFACE_PAIRS = [\n  ['card', 'card-foreground'],\n  ['card', 'card-muted-foreground'],  // ← duplicate surface 'card'\n  ['popover', 'popover']              // ← surface === foreground\n] as const\n\n// after — distinct, unique surfaces\nexport const SHADCN_SURFACE_PAIRS = [\n  ['card', 'card-foreground'],\n  ['popover', 'popover-foreground']\n] as const","handlingStrategy":"validation","validationCode":"// Check surface pairs for duplicates and self-references before committing\nimport { SHADCN_SURFACE_PAIRS, CHERRY_PRODUCT_SURFACE_PAIRS } from './theme-contract'\n\nfunction checkSurfacePairs(label: string, pairs: ReadonlyArray<readonly [string, string]>): void {\n  const surfaces = new Set<string>()\n  for (const [surface, foreground] of pairs) {\n    if (surface === foreground) throw new Error(`${label}: ${surface} pairs with itself`)\n    if (surfaces.has(surface)) throw new Error(`${label}: duplicate surface ${surface}`)\n    surfaces.add(surface)\n  }\n}\n\ncheckSurfacePairs('Shadcn', SHADCN_SURFACE_PAIRS)\ncheckSurfacePairs('product', CHERRY_PRODUCT_SURFACE_PAIRS)","typeGuard":null,"tryCatchPattern":null,"preventionTips":["When adding a surface pair, verify the surface name isn't already used in another pair.","Ensure surface and foreground are always distinct tokens.","Run the theme contract validator after any change to surface pair arrays.","Use a consistent naming pattern (e.g., surface + '-foreground') to avoid mismatches."],"tags":["theme-contract","build-validation","surface-pairs","duplicates","configuration"],"backgroundTag":null,"analyzedSha":"726446b54cd69ffe51a276638672f6d95ca0768c","analyzedAt":"2026-08-12T17:30:37.448Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}