CherryHQ/cherry-studio · error · Error
[theme-contract] foundation cannot declare runtime input ${d
Error message
[theme-contract] foundation cannot declare runtime input ${declaration.name} What it means
Thrown by assertLayerDependencies when a foundation source file (tokens/colors/primitive.css, providers.css, status-legacy.css, spacing.css, radius.css, typography.css) declares a variable whose name starts with the runtime-input prefix `--cs-theme-`. Runtime inputs are host-written internal values owned exclusively by theme-input.css; the foundation layer must not produce them.
Source
Thrown at packages/ui/scripts/validate-theme-contract.ts:242
function assertLayerDependencies(sources: ThemeContractSources): void {
const runtimeVariablePrefix = '--cs-theme-'
const runtimeVariables = new Set(RUNTIME_THEME_INPUT_TOKENS.map((token) => `--cs-theme-${token}`))
const officialVariables = new Set(SHADCN_VARIABLE_TOKENS.map((token) => `--${token}`))
const productVariables = new Set(CHERRY_PRODUCT_VARIABLE_TOKENS.map((token) => `--${token}`))
const foundationEntries: SourceEntry[] = [
['tokens/colors/primitive.css', sources.primitiveColors],
['tokens/colors/providers.css', sources.providerColors],
['tokens/colors/status-legacy.css', sources.statusLegacyColors],
['tokens/spacing.css', sources.spacing],
['tokens/radius.css', sources.radius],
['tokens/typography.css', sources.typography]
]
for (const [sourceName, source] of foundationEntries) {
for (const declaration of extractDeclarations(source, sourceName)) {
if (declaration.name.startsWith(runtimeVariablePrefix)) {
throw new Error(`[theme-contract] foundation cannot declare runtime input ${declaration.name}`)
}
for (const reference of extractReferences(declaration.value, declaration.source)) {
if (
reference.startsWith(runtimeVariablePrefix) ||
officialVariables.has(reference) ||
productVariables.has(reference) ||
reference.startsWith('--color-') ||
reference.startsWith('--app-')
) {
throw new Error(`[theme-contract] foundation ${declaration.name} cannot depend on upper-layer ${reference}`)
}
}
}
}
for (const declaration of extractDeclarations(sources.themeInput, 'theme-input.css')) {
if (!runtimeVariables.has(declaration.name)) {
throw new Error(`[theme-contract] theme-input.css declares unregistered runtime input ${declaration.name}`)View on GitHub (pinned to 726446b54c)
Solutions
- Move the declaration into theme-input.css (the only legal owner of --cs-theme-* names).
- If the value is a static foundation token rather than host-written, rename it to the foundation --cs-* namespace and keep it in the token file.
- If it is a genuinely new runtime input, also register the unprefixed name in RUNTIME_THEME_INPUT_TOKENS in scripts/theme-contract.ts.
Example fix
// before (tokens/colors/primitive.css)
:root { --cs-theme-speculative: hotpink; }
// after — move to theme-input.css and register, OR rename
// theme-input.css
:root { --cs-theme-speculative: var(--cs-speculative); } Defensive patterns
Strategy: validation
Validate before calling
// Reject any --cs-theme-* name appearing in foundation sources before validating.
const foundationSources = [sources.primitiveColors, sources.providerColors, sources.statusLegacyColors, sources.spacing, sources.radius, sources.typography]
const offenders = foundationSources.flatMap((s) => [...s.matchAll(/(--cs-theme-[a-z0-9-]+)\s*:/g)].map((m) => m[1]))
if (offenders.length) throw new Error(`foundation must not declare runtime inputs: ${offenders.join(', ')}`) Try / catch
try {
validateThemeContractSources(sources)
} catch (error) {
if (error instanceof Error && /foundation cannot declare runtime input/.test(error.message)) {
console.error(error.message)
process.exitCode = 1
return
}
throw error
} Prevention
- Reserve the --cs-theme- prefix exclusively for theme-input.css.
- Foundation tokens use the --cs- prefix (or --spacing-/--radius- etc.), never --cs-theme-.
- When adding a runtime input, edit theme-input.css and RUNTIME_THEME_INPUT_TOKENS together.
When it happens
Trigger: Adding `:root { --cs-theme-primary: hotpink; }` to primitive.css; pasting a runtime input declaration into a token file while prototyping; misunderstanding the --cs-theme- prefix as a general foundation namespace.
Common situations: Adding a new runtime-themable knob and putting it in the wrong file; copy-paste from theme-input.css into a token file.
Related errors
- [theme-contract] foundation ${declaration.name} cannot depen
- [theme-contract] runtime input ${declaration.name} cannot de
- [theme-contract] runtime input ${declaration.name} cannot de
- [theme-contract] shadcn.css cannot own runtime input ${decla
- [theme-contract] Shadcn ${declaration.name} cannot depend on
AI-assisted analysis of CherryHQ/cherry-studio@726446b54c (2026-08-12).
Data as JSON: /api/errors/0f60cf3fea67d875.
Report an issue: GitHub.