CherryHQ/cherry-studio · error · Error
[theme-contract] ${label} is missing root declarations: ${mi
Error message
[theme-contract] ${label} is missing root declarations: ${missing.join(', ')} What it means
Thrown by assertRequiredDeclarations when a registered contract token list (runtime inputs, Shadcn variables, or product variables) has entries with no matching `--<prefix><token>` declaration in the :root block of the owning source. The contract is closed: every registered token MUST be declared in :root so consumers always resolve. The message lists every missing prefixed name.
Source
Thrown at packages/ui/scripts/validate-theme-contract.ts:165
)
}
declarations.set(declaration.name, declaration)
}
}
return declarations
}
function assertRequiredDeclarations(
label: string,
declarations: Map<string, Declaration>,
variableNames: readonly string[],
prefix: string
): void {
const missing = variableNames.map((name) => `${prefix}${name}`).filter((name) => !declarations.has(name))
if (missing.length > 0) {
throw new Error(`[theme-contract] ${label} is missing root declarations: ${missing.join(', ')}`)
}
}
function assertCompatibilityTokensDeclared(
label: string,
tokenNames: readonly string[],
source: string,
sourceName: string
): void {
const declarations = new Set(extractDeclarations(source, sourceName).map((declaration) => declaration.name))
const missing = tokenNames.map((token) => `--cs-${token}`).filter((name) => !declarations.has(name))
if (missing.length > 0) {
throw new Error(`[theme-contract] ${label} references missing foundation variables: ${missing.join(', ')}`)
}
}
function assertReferencesResolve(mode: string, declarations: Map<string, Declaration>): void {View on GitHub (pinned to 726446b54c)
Solutions
- Restore the missing declaration(s) into the :root block of the source named in the label (label maps to file: 'Shadcn contract in shadcn.css' -> shadcn.css, 'product contract in product.css' -> product.css, 'runtime theme inputs' -> the aggregated :root graph).
- If a token was deliberately removed from the CSS, also remove it from the corresponding token registry in scripts/theme-contract.ts (SHADCN_VARIABLE_TOKENS / CHERRY_PRODUCT_VARIABLE_TOKENS / RUNTIME_THEME_INPUT_TOKENS).
- Re-run `pnpm --filter @cherrystudio/ui theme:check`.
Example fix
// before — :root block of shadcn.css with --background removed
// after
:root {
--background: var(--cs-background);
} Defensive patterns
Strategy: validation
Validate before calling
// Pre-check that every registered token has a :root declaration in its owning source.
const required = new Set([...SHADCN_VARIABLE_TOKENS, ...CHERRY_PRODUCT_VARIABLE_TOKENS].map((t) => `--${t}`))
const declared = new Set([...sources.shadcn.matchAll(/^[\s{;]*(--[a-z0-9-]+)\s*:/gm), ...sources.product.matchAll(/^[\s{;]*(--[a-z0-9-]+)\s*:/gm)].map((m) => m[1]))
const missing = [...required].filter((n) => !declared.has(n))
if (missing.length) throw new Error(`missing :root declarations: ${missing.join(', ')}`) Try / catch
try {
validateThemeContractSources(sources)
} catch (error) {
if (error instanceof Error && /is missing root declarations/.test(error.message)) {
console.error(error.message) // lists the exact missing variables
process.exitCode = 1
return
}
throw error
} Prevention
- When adding a token to a SHADCN_*/CHERRY_PRODUCT_*/RUNTIME_THEME_INPUT_TOKENS registry, author its :root declaration in the same commit.
- Never move a required declaration out of :root into a nested selector.
- Treat the lint/build:check gate as non-optional; it catches this before push.
When it happens
Trigger: Deleting `--background: var(--cs-background);` from shadcn.css :root; deleting `--chat-user: ...;` from product.css :root; declaring a required variable only inside `.dark` and not in `:root`.
Common situations: Cleaning up 'unused' variables that are actually contract-mandated; moving a declaration into a nested selector; rebasing across a commit that added a new token to SHADCN_VARIABLE_TOKENS / CHERRY_PRODUCT_VARIABLE_TOKENS without authoring the CSS.
Related errors
- [theme-contract] ${declaration.name} is defined twice in ${s
- [theme-contract] ${label} references missing foundation vari
- [theme-contract] ${mode} ${declaration.name} in ${declaratio
- [theme-contract] ${mode} variable cycle: ${[...stack.slice(c
- [theme-contract] foundation cannot declare runtime input ${d
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/fafbf1149720d6c7.
Report an issue: GitHub.