---
title: "Troubleshooting"
description: "The failures that actually happen, each with its symptom, its cause and its fix, plus an honest note about the ones whose exact error text we have not seen yet."
url: "https://opsinjs.pensievelabs.org/start/troubleshooting"
source: "https://opsinjs.pensievelabs.org/start/troubleshooting.md"
section: "Start here"
kind: "guide"
reviewed: "2026-09-20"
reviewer: "engineering"
aliases: ["troubleshooting", "common errors", "it does not work", "unstyled components"]
---

> Elements written as `<PascalCase … />` below are opsinjs documentation
> components. Their attributes are the content: the values they render are
> generated from `tokens/*.json` and `registry/catalogue.ts` and are
> published separately at https://opsinjs.pensievelabs.org/r/index.json and under the Reference
> section.
> Nothing is missing from this page. The data simply does not live in
> the prose.

<PageTemplate kind="guide" />

## Overview [#overview]

This page is organised by *when* the failure happens, because that is the fastest
discriminator: a problem at install time has a completely different cause set from
one that only appears in a production build. Each entry states a symptom you can
match, the cause, and the fix.

<Todo owner="engineering">
  This page is specified to quote the exact error text for each failure, anchored
  by error code. Half of that is now possible and is not done here. The
  implemented components emit their development warnings under `OPSIN-` codes
  declared in `tokens/errors.json`, and
  [Error codes](../handbook/error-codes.mdx) is generated from that file, so a
  reader pasting a code into a search box should be landing there. This page
  should quote and link the runtime entries rather than describing them. The
  install-time and build-time failures still have no captured text, because
  nothing has been published and so nobody has produced one; inventing
  plausible-looking error text is worse than omitting it, since it makes the page
  unsearchable in exactly the situation it exists for. Those entries stay matched
  by symptom.
</Todo>

## Failures before anything is installed [#failures-before-anything-is-installed]

### Nothing is published yet [#nothing-is-published-yet]

**Symptom.** A `shadcn add @opsinjs/...` command fails with a network or DNS
error rather than a registry one, and `npm install` finds no `@opsinjs` package.

**Cause.** Two separate absences, and only the first is permanent. There is no
npm package and there is not going to be one: opsinjs is distributed as source
copied in through the shadcn CLI, so `npm install` is not a step that is missing.
Separately, `opsinjs.pensievelabs.org` is the canonical host every command on
this site names, and the URL the `registries` entry is written with. That
subdomain is not attached to a deployment yet, so a `components.json` pointing
there fails before the CLI reaches a registry at all.

**Fix.** Point `@opsinjs` at a host that is actually serving the registry items
under `/r`; the sixty built components resolve from it with their
complete source, and
[Registry](../registry/index.mdx) says which components those are. If instead the
CLI reports an *unknown* registry rather than a network failure, the cause is not
this one at all: it is **The registry namespace is not configured**, under
failures at install time below.

### An agent generated opsinjs code that does not exist [#an-agent-generated-opsinjs-code-that-does-not-exist]

**Symptom.** You have a file importing `@/components/opsinjs/...`, nothing
resolves, and nobody on the team remembers writing it.

**Cause.** A coding assistant read a component page and generated against it. Two
different things look identical here, so check which one you have before you
delete anything. Every one of the sixty catalogue components is built, so an
import whose id is in the catalogue names real code that has simply never been
copied into this project. The fix is to run the add. An import whose id is absent
from the catalogue was invented outright, and there is nothing to add.

**Fix.** Check the id against [Registry](../registry/index.mdx), or fetch
`/r/index.json` and look for the item. Present in the catalogue: run the add.
Absent: delete the file, because the assistant invented a name that no
specification stands behind, then point the assistant at
[Rules for agents](../agents/rules-for-agents.mdx), which states the constraint in
the form an agent will actually follow.

## Failures at install time [#failures-at-install-time]

### The registry namespace is not configured [#the-registry-namespace-is-not-configured]

**Symptom.** The CLI reports an unknown or unresolvable registry for `@opsinjs`,
while unnamespaced `shadcn add` commands work normally.

**Cause.** `components.json` has no `registries` entry mapping `@opsinjs` to the
registry URL.

**Fix.** Add it. Every field opsinjs reads, and the exact entry, are in
[components.json](./installation/components-json.mdx).

### Every copied component fails on `@/lib/utils` [#every-copied-component-fails-on-libutils]

**Symptom.** The files landed where you expected, and the typechecker reports the
same error once per copied file: it cannot find the module `@/lib/utils`. Nothing
else is wrong.

**Cause.** `shadcn init` was never run in this project. Every opsinjs component
imports `cn` from `@/lib/utils`, and the registry does not ship that file on
purpose. A third copy would collide with the one a project already has and may
have extended, which is
[ADR 0010](../project/decisions/0010-shared-code-ships-with-every-item.mdx).
`init` is what creates it, so a project that went straight to `add` gets every
component failing on the same missing module.

**Fix.** Run `npx shadcn@latest init`. Where that is not possible, add the `cn`
helper by hand at whatever path `aliases.utils` names. It is shadcn's own
`utils` item, not an opsinjs one. The tell is that the error is identical on
every copied file at once: a wrong alias breaks one path, an absent file breaks
all of them.

### Files land in the wrong directory [#files-land-in-the-wrong-directory]

**Symptom.** The CLI reports success, but the component is not where you expected,
or an import path in the copied file does not resolve.

**Cause.** The `aliases` block in `components.json` does not match your actual
`tsconfig` paths. The CLI writes to the alias; your bundler resolves the tsconfig
path; when they disagree, nothing reconciles them.

**Fix.** Make `components.json` aliases and `tsconfig.json` paths agree. In a
workspace, both must be right in the *app* that is being written to, not in the
repository root. [Monorepo](./installation/monorepo.mdx).

### The Node version is too old [#the-node-version-is-too-old]

**Symptom.** The CLI or a build script fails early with a syntax or unsupported
error, before doing any real work.

**Cause.** opsinjs tooling targets a current Node LTS and does not transpile
itself.

**Fix.** Upgrade Node. This is worth checking first whenever a failure happens
suspiciously early, because the resulting message rarely names the version as the
cause.

## Failures at build time [#failures-at-build-time]

### Components render completely unstyled [#components-render-completely-unstyled]

**Symptom.** The markup is correct, the component is in the tree, and it has no
styling at all. Often works in development and fails in a production build, which
makes it look like a bundler problem.

**Cause.** Tailwind v4 discovers classes by scanning declared sources. opsinjs
components are copied into *your* repository, so the directory you copied them into
must be scanned. If it is not, the classes are never generated.

**Fix.** Add an `@source` line for the directory. This is the single most common
opsinjs setup failure by a wide margin, and no opsinjs setting can fix it. It is a
Tailwind configuration fact. [Monorepo](./installation/monorepo.mdx) and
[Tailwind v4](../theming/tailwind-v4.mdx).

### Colours are wrong, or fall back to browser defaults [#colours-are-wrong-or-fall-back-to-browser-defaults]

**Symptom.** Layout and spacing are correct; colours are not. Inspecting an element
shows a custom property resolving to nothing.

**Cause.** The token layer is missing, or imported in the wrong position. CSS order
in opsinjs is load-bearing: the token layer must come after Tailwind and after any
base reset, and before your own overrides.

**Fix.** Check the import position against the installation page for your
framework. [Next.js](./installation/next.mdx) states the exact position, and it is
the same rule everywhere.

### A server component fails because something needs client-side React [#a-server-component-fails-because-something-needs-client-side-react]

**Symptom.** The build fails on an import chain that reaches an opsinjs component,
complaining that a hook or context is being used on the server.

**Cause.** You imported the interactive half of a component into a server tree.
Most opsinjs components are server-renderable; a minority are not, and are
shipped as separate files precisely so this is avoidable. Those are the ones
that manage focus, hold open state or observe the viewport.

**Fix.** Import the display half, or mark the *leaf* as a client component. Marking
the whole page is the reflex fix and it silently moves your entire tree to the
client. [Framework support](./framework-support.mdx#know-where-the-client-boundary-falls).

### The generated docs tables are empty after a checkout [#the-generated-docs-tables-are-empty-after-a-checkout]

**Symptom.** Token tables, prop tables and contrast reports render placeholders
naming a script.

**Cause.** Generated artifacts are not committed in a usable state until the
generate step has run.

**Fix.** Run the generate step. This one is specific to working *on* the
documentation rather than using opsinjs;
[Contributing](../handbook/contributing/index.mdx) covers the sequence.

## Failures in the browser [#failures-in-the-browser]

### Hydration mismatch on a time or a formatted number [#hydration-mismatch-on-a-time-or-a-formatted-number]

**Symptom.** React reports a hydration mismatch, and the differing text is a
relative time, a date or a formatted value.

**Cause.** Time zone or locale differs between the rendering server and the
browser. This is not specific to opsinjs, but health interfaces hit it constantly
because they are full of timestamps.

**Fix.** Use the components' documented hydration-safe contract rather than
formatting inline. `RelativeTime` and `Value` take responsibility for this case;
`Date.prototype.toLocaleString` in a render function does not.

### A surface is opaque, or a transition does not animate [#a-surface-is-opaque-or-a-transition-does-not-animate]

**Symptom.** Backdrop blur is missing, or a transition happens instantly.

**Cause.** Almost always an accessibility preference, not a browser gap:
`prefers-reduced-transparency` and `prefers-reduced-motion` are honoured throughout
the system, and both fall back to *designed* states rather than to nothing.

**Fix.** Check the operating system setting before investigating anything else. If
the preference is off and the effect is still missing, then it is a browser
capability question: [Browser support](./browser-support.mdx).

## Verify it worked [#verify-it-worked]

Before reporting anything, confirm the environment is what you think it is. Most
reports that turn out not to be bugs fail one of these four:

1. **Node version** is current LTS or later.
2. **React version** is 19 or later, with only one copy of React in the tree.
3. **Tailwind version** is v4, with your component directory covered by `@source`.
4. **Stylesheet order** is the token layer after Tailwind, before your overrides.

If all four are right and the symptom persists, you have something worth
reporting.

## Troubleshooting [#troubleshooting]

**None of the entries above matches.** Reduce it. A minimal reproduction that
fails is worth more than a long description of a large application that fails, and
producing one usually finds the cause before you finish.

**The symptom is intermittent.** Suspect caching first: a stale build cache, a
stale Tailwind scan, a service worker. Clear each in turn rather than all at once,
so the answer is still informative.

**It only fails in production.** Suspect source scanning and environment
differences before suspecting opsinjs. Development and production differ most in
what they scan and what they tree-shake.

**You are sure it is a defect.** Report it with the four version numbers above and
a minimal reproduction. [Community](../project/community.mdx) has where.

## Next [#next]

* [FAQ](./faq.mdx) is for questions that are not failures.
* [Installation](./installation/index.mdx) covers the setup this page assumes.
* [Community](../project/community.mdx) is where to report something this page
  does not cover.
