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

  1. Move the declaration into theme-input.css (the only legal owner of --cs-theme-* names).
  2. 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.
  3. 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

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


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