# What Causes Token Reference Resolution Errors in DESIGN.md: 7 Root Causes Explained

> Troubleshoot token reference resolution errors in DESIGN.md. Discover 7 root causes including missing tokens, circular dependencies, and syntax issues to fix your linter.

- Repository: [Google Labs Code/design.md](https://github.com/google-labs-code/design.md)
- Tags: deep-dive
- Published: 2026-06-30

---

**Token reference resolution errors occur when the DESIGN.md CLI linter cannot map a `{path.to.token}` reference to a concrete primitive value, caused by missing tokens, circular dependencies, excessive nesting depth beyond 50 levels, invalid syntax, or type mismatches.**

Token reference resolution errors in [`google-labs-code/design.md`](https://github.com/google-labs-code/design.md/blob/main/google-labs-code/design.md) occur when the CLI linter cannot reconcile the `{path.to.token}` syntax with a concrete primitive value in the symbol table. These failures surface during the recursive `resolveReference` walk in [`packages/cli/src/linter/model/handler.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/model/handler.ts) and are aggregated by the `brokenRef` rule into specific lint findings.

## How the Resolution Pipeline Works

The linter processes references in three distinct phases. First, it constructs a **symbol table** containing every resolved token across colors, typography, spacing, and other primitive definitions. Second, it calls `resolveReference` recursively to walk this table and dereference each token path. Finally, the `brokenRef` rule in [`packages/cli/src/linter/linter/rules/broken-ref.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/linter/rules/broken-ref.ts) collects any failures and emits specific findings.

An error surfaces when `resolveReference` returns `null` or when the resolved value violates the spec constraints defined in [`docs/spec.md`](https://github.com/google-labs-code/design.md/blob/main/docs/spec.md) at lines 86-87.

## The Seven Causes of Token Reference Resolution Errors

### 1. Missing or Undefined Tokens

The most common error occurs when a reference points to a token that does not exist in the symbol table. In [`handler.ts`](https://github.com/google-labs-code/design.md/blob/main/handler.ts), the `resolveReference` function checks `symbolTable.get(path)`, and when this returns `undefined`, the function returns `null` immediately.

The `brokenRef` rule then adds this to the component's `unresolvedRefs` list and emits the finding: "Reference `{...}` does not resolve to any defined token."

```yaml
components:
  button-primary:
    backgroundColor: "{colors.unknown-60}"

```

Running `designmd lint` produces:

```

components.button-primary → Reference {colors.unknown-60} does not resolve to any defined token.

```

*Source:* `brokenRef` aggregates findings from `comp.unresolvedRefs` at lines 25-31 in [`broken-ref.ts`](https://github.com/google-labs-code/design.md/blob/main/broken-ref.ts).

### 2. Circular Reference Chains

When tokens reference each other in a cycle, the resolver detects the loop through a `visited` Set. In [`handler.ts`](https://github.com/google-labs-code/design.md/blob/main/handler.ts) at lines 84-85, if `visited.has(path)` returns true during recursion, `resolveReference` returns `null` immediately to prevent infinite loops.

```yaml
colors:
  a: "{colors.b}"
  b: "{colors.a}"

```

The linter reports the same "does not resolve" message because it cannot follow the chain to a concrete primitive value.

### 3. Exceeding Maximum Reference Depth

The resolver enforces a safety limit to prevent stack overflow. At line 83 in [`handler.ts`](https://github.com/google-labs-code/design.md/blob/main/handler.ts), the code checks `depth > MAX_REFERENCE_DEPTH` (default 50) and returns `null` if the chain is too deep.

```yaml

# 60 chained references (MAX_REFERENCE_DEPTH = 50)

colors:
  a0: "{colors.a1}"
  a1: "{colors.a2}"
  # ... a58: "{colors.a59}"

  a59: "#ff0000"

```

When the depth surpasses 50, the resolution aborts and triggers an unresolved-reference error.

### 4. Invalid Reference Syntax

Tokens must use the `{path}` syntax to be recognized as references. Plain values without surrounding braces are never sent to `resolveReference`; they are recorded as literal strings. When a component property expects a reference but receives a malformed token string, the parser flags it in `brokenRef` as unresolved.

### 5. References to Groups Instead of Primitives

The specification demands that references outside of components must resolve to primitive values (colors, dimensions, etc.), not to groups or maps. According to [`docs/spec.md`](https://github.com/google-labs-code/design.md/blob/main/docs/spec.md) at lines 86-87, when `resolveReference` returns a group object rather than a primitive, the linter flags a mismatch.

```yaml
components:
  card:
    typography: "{typography}"   # `typography` is a map, not a primitive token

```

This violates the spec requirement for primitive resolution in most contexts.

### 6. Unknown Component Sub-Tokens

Components can only use whitelisted property names. The `brokenRef` rule checks each property against `VALID_COMPONENT_SUB_TOKENS` at lines 34-40 in [`broken-ref.ts`](https://github.com/google-labs-code/design.md/blob/main/broken-ref.ts). Unrecognized properties trigger a warning rather than a hard error.

```yaml
components:
  button-primary:
    unknownProp: "{colors.primary}"

```

Output:

```

components.button-primary.unknownProp → 'unknownProp' is not a recognized component sub-token.

```

### 7. Type Mismatches in Reference Chains

Even when a reference resolves successfully, the final value undergoes type validation. If a property expects a `color` but the resolved chain ends with a `typography` object, the component-resolution loop pushes an unresolved entry. This typically produces the same "does not resolve" finding, though the underlying cause is a semantic type mismatch rather than a missing symbol.

## How the Linter Aggregates Errors

The `brokenRef` rule serves as the central collector for all resolution failures. Located at [`packages/cli/src/linter/linter/rules/broken-ref.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/linter/rules/broken-ref.ts), this rule:

1. Iterates through each component's `unresolvedRefs` list populated by failed `resolveReference` calls
2. Checks property names against the `VALID_COMPONENT_SUB_TOKENS` whitelist
3. Emits structured findings with file paths and line numbers

The rule distinguishes between hard errors (unresolved references) and warnings (unknown sub-tokens), though both indicate configuration issues that prevent proper token resolution.

## Summary

- **Missing tokens** occur when `symbolTable.get(path)` returns `undefined` in [`handler.ts`](https://github.com/google-labs-code/design.md/blob/main/handler.ts), causing `resolveReference` to return `null`.
- **Circular references** are caught by the `visited` Set check at lines 84-85 in [`handler.ts`](https://github.com/google-labs-code/design.md/blob/main/handler.ts) before they cause infinite recursion.
- **Depth limits** trigger when reference chains exceed 50 levels, as enforced at line 83 in [`handler.ts`](https://github.com/google-labs-code/design.md/blob/main/handler.ts).
- **Invalid syntax** bypasses the resolver entirely, resulting in plaintext values where references are expected.
- **Group references** fail because the spec requires primitive values outside component contexts.
- **Unknown sub-tokens** are flagged by the whitelist check in [`broken-ref.ts`](https://github.com/google-labs-code/design.md/blob/main/broken-ref.ts) at lines 34-40.
- **Type mismatches** occur when resolved chains end with incompatible primitive types for the target property.

## Frequently Asked Questions

### How do I fix a "does not resolve to any defined token" error?

Verify that the token path inside the braces exactly matches a defined token in your symbol table. Check for typos in the path segments and ensure the referenced token is defined before it is used. If the token exists in a different file, confirm that file is being processed by the linter.

### What is the maximum depth for token references in DESIGN.md?

The default maximum depth is **50 levels**, defined by `MAX_REFERENCE_DEPTH` in [`packages/cli/src/linter/model/handler.ts`](https://github.com/google-labs-code/design.md/blob/main/packages/cli/src/linter/model/handler.ts). If your design system requires deeper nesting, you must modify this constant in the source code, though the linter prevents this to avoid stack overflow and performance degradation.

### Why does my reference work in one component but fail in another?

Component properties are restricted to whitelisted sub-tokens defined in `VALID_COMPONENT_SUB_TOKENS`. If a property name works in one context but fails in another, check that the property is recognized by the `brokenRef` rule at lines 34-40 in [`broken-ref.ts`](https://github.com/google-labs-code/design.md/blob/main/broken-ref.ts). Additionally, ensure the reference resolves to a primitive value appropriate for that specific property type.

### Can I reference a group of tokens instead of a single value?

Outside of component definitions, the specification requires references to resolve to primitive values (colors, dimensions, etc.) according to [`docs/spec.md`](https://github.com/google-labs-code/design.md/blob/main/docs/spec.md) at lines 86-87. Referencing an entire group or map object will trigger a resolution error unless the specific implementation explicitly supports group references for that property.