CherryHQ/cherry-studio · error · Error
[theme-contract] Tailwind product color ${token} is missing
Error message
[theme-contract] Tailwind product color ${token} is missing from the product contract What it means
Thrown by validateThemeContractSources when a token in CHERRY_PRODUCT_COLOR_TOKENS (the Tailwind-exposed color surface) is missing from CHERRY_PRODUCT_VARIABLE_TOKENS (the CSS contract). Every Tailwind color utility must be backed by a declared product CSS variable so the utility and the contract cannot drift.
Source
Thrown at packages/ui/scripts/validate-theme-contract.ts:330
assertUnique('compatibility semantic colors', COMPATIBILITY_SEMANTIC_COLOR_TOKENS)
assertUnique('compatibility status colors', COMPATIBILITY_STATUS_COLOR_TOKENS)
assertUnique('compatibility colors', COMPATIBILITY_COLOR_TOKENS)
const productVariables = new Set<string>(CHERRY_PRODUCT_VARIABLE_TOKENS)
const shadcnVariables = new Set<string>(SHADCN_VARIABLE_TOKENS)
const shadcnVariableNames = new Set<string>(SHADCN_VARIABLE_TOKENS.map((token) => `--${token}`))
const productVariableNames = new Set<string>(CHERRY_PRODUCT_VARIABLE_TOKENS.map((token) => `--${token}`))
const canonicalColors = new Set<string>([...SHADCN_COLOR_TOKENS, ...CHERRY_PRODUCT_COLOR_TOKENS])
for (const token of CHERRY_PRODUCT_VARIABLE_TOKENS) {
if (shadcnVariables.has(token)) {
throw new Error(`[theme-contract] product variable ${token} overlaps the official Shadcn contract`)
}
}
for (const token of CHERRY_PRODUCT_COLOR_TOKENS) {
if (!productVariables.has(token)) {
throw new Error(`[theme-contract] Tailwind product color ${token} is missing from the product contract`)
}
}
for (const token of COMPATIBILITY_COLOR_TOKENS) {
if (canonicalColors.has(token)) {
throw new Error(`[theme-contract] compatibility color ${token} overlaps the canonical color contract`)
}
}
assertCompatibilityTokensDeclared(
'compatibility semantic colors',
COMPATIBILITY_SEMANTIC_COLOR_TOKENS,
sources.providerColors,
'tokens/colors/providers.css'
)
assertCompatibilityTokensDeclared(
'compatibility status colors',
COMPATIBILITY_STATUS_COLOR_TOKENS,
sources.statusLegacyColors,
'tokens/colors/status-legacy.css'View on GitHub (pinned to 726446b54c)
Solutions
- Add the token to CHERRY_PRODUCT_VARIABLE_TOKENS in scripts/theme-contract.ts AND declare `--<token>` in product.css.
- If the Tailwind utility is not needed, remove the token from CHERRY_PRODUCT_COLOR_TOKENS instead.
- Re-run `pnpm --filter @cherrystudio/ui theme:check`.
Example fix
// before — theme-contract.ts
export const CHERRY_PRODUCT_COLOR_TOKENS = ['new-role', ...] as const
// CHERRY_PRODUCT_VARIABLE_TOKENS does not include 'new-role'
// after
export const CHERRY_PRODUCT_VARIABLE_TOKENS = ['new-role', ...] as const
// and in product.css: :root { --new-role: var(--cs-...); } Defensive patterns
Strategy: validation
Validate before calling
// Every Tailwind color token must be a declared product variable.
const productVars = new Set(CHERRY_PRODUCT_VARIABLE_TOKENS)
const missing = CHERRY_PRODUCT_COLOR_TOKENS.filter((t) => !productVars.has(t))
if (missing.length) throw new Error(`color tokens missing from product contract: ${missing.join(', ')}`) Try / catch
try {
validateThemeContractSources(sources)
} catch (error) {
if (error instanceof Error && /Tailwind product color .* is missing from the product contract/.test(error.message)) {
console.error(error.message)
process.exitCode = 1
return
}
throw error
} Prevention
- Adding a token to CHERRY_PRODUCT_COLOR_TOKENS requires the same token in CHERRY_PRODUCT_VARIABLE_TOKENS and a `--<token>` declaration in product.css.
- Treat the two lists as a pair; only add a Tailwind utility when the CSS variable exists.
- Run theme:check after editing either list.
When it happens
Trigger: Adding 'new-role' to CHERRY_PRODUCT_COLOR_TOKENS in scripts/theme-contract.ts to generate a Tailwind utility, without also adding 'new-role' to CHERRY_PRODUCT_VARIABLE_TOKENS and declaring `--new-role` in product.css.
Common situations: Exposing a new Tailwind color utility and forgetting the contract half; trimming the variable list but leaving the color list.
Related errors
- [theme-contract] theme-input.css declares unregistered runti
- [theme-contract] product variable ${token} overlaps the offi
- [theme-contract] compatibility color ${token} overlaps the c
- [theme-contract] shadcn.css declares unregistered Shadcn var
- [theme-contract] product.css declares unregistered product v
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/fe27ec5da3012895.
Report an issue: GitHub.