Troubleshooting

The most common issues during the beta come down to three things: the bridge isn't running, the URLs don't match, or preview mode isn't enabled. Work through these first.

The plugin can't reach the bridge

  • Confirm sorb dev is running and note the origin it prints (default http://localhost:7777).
  • In the plugin's Settings, make sure Bridge origin matches exactly — including http:// and the port.
  • The bridge binds to 127.0.0.1 and rejects cross-site writes by design. If you're connecting from a hosted setup, use an org key instead of a local origin (see Cloud & accounts).

Preview doesn't appear in my app

  • Preview mode is off by default in @sorb/leaf. Enable it and allowlist the bridge origin you're previewing from.
  • Check the App URL in Settings points at your running dev server (e.g. http://localhost:5173).
  • Make sure your app is actually wrapped in SorbProvider.

Preview banner warnings

The banner at the bottom of your app has three states, and each one maps to a specific fetch or vocabulary failure.

Red — "Sorb preview unavailable"

Shown when a deliberately-requested ?preview=<id> could not be loaded. The app has already fallen back to its committed tokens — the banner is the only sign the fetch failed — and this line prints once to the console regardless of build mode:

[@sorb/leaf] preview "V1StGXR8" could not be loaded (not_found) — it may not be visible to this app's key; it may belong to a different project. Falling back to committed tokens.

The outcome in previewError (readable via usePreviewState()) tells you which of three things happened:

OutcomeHTTP statusCauseFix
not_found404The id belongs to a different project, or the preview expired or was deleted. The bridge answers 404 for both a genuinely-expired id and a cross-tenant id — it never confirms someone else's preview exists.Re-run Preview from the plugin to mint a fresh id; confirm the plugin and the app are paired to the same project.
unauthorized401 or 403The key in preview.key cannot read this project, or it's a write-scoped key used where a read key was expected.Check preview.key against the key issued for this project in Cloud & accounts.
networkany other status, or fetch itself rejectedThe bridge origin didn't answer — sorb dev isn't running, the port is wrong, or a hosted bridge is unreachable.Confirm preview.origin matches what sorb dev printed, or that bridge.sorbcloud.com is reachable.

Amber — "Sorb preview active — may not re-skin"

The preview loaded and applied fine, but none of its token names start with a prefix you declared in preview.expectPrefixes — the classic silent failure where colors change in the token store but nothing moves on screen. One warning logs when this fires:

[Sorb] preview "V1StGXR8" applied 42 tokens but none match expected prefixes ["bs-"] — the app may not visibly re-skin (token-vocabulary mismatch).

Two causes, in order of likelihood:

  1. The keys carry a leading --. If you built the preview body yourself (curl, a script, a test), keys must be the bare token names — button-primary-bg-default, not --button-primary-bg-default. The SDK prepends -- on write, so a prefixed key becomes ----button-primary-bg-default on <html>, matches no expected prefix, and touches nothing your CSS reads. Check with document.documentElement.getAttribute('style') in the console: a value starting ---- is this cause. Re-post the body without the dashes.
  2. Vocabulary mismatch. Compare the prefixes the plugin is writing against preview.expectPrefixes in your SorbConfig — the app was scaffolded for one UI kit's custom-property vocabulary (e.g. --bs-*) while the token set targets another. An empty preview (zero tokens applied) is not this failure — it falls through to the red state's not_found/network handling instead.

Blue — "Sorb preview active"

Healthy: the preview loaded and matched at least one expected prefix (or no prefixes were declared). Nothing to fix.

sorb-hello doesn't answer a diagnostics ping

If you postMessage({ type: 'sorb-ping' }) at a running app's window from your own tooling and never get a sorb-hello reply, the sending window's origin isn't on the diagnostics allowlist. The leaf never posts unsolicited — it only answers a ping from an allowlisted origin, and always replies to the exact event.origin (never '*'). By default only https://app.sorbcloud.com (and its staging counterpart) are allowlisted; extend it for a self-hosted dashboard with config.diagnostics.allowedOrigins. The reply itself only ever carries the namespace, the key's last four characters, the SDK version, the bridge origin, and the last preview outcome — never a full key, and no auth/routing decision is ever derived from it.

sorb check reports drift or a binding mismatch

sorb check re-runs the Style Dictionary build and diffs the fresh snapshot for drift, binding mismatches, off-role bindings, or deprecated tokens still in use, exiting non-zero so CI can fail on it. It makes no compliance claim beyond what it measured — read the reported findings; there's no separate "explain this" flag. Run it locally with the same flags CI uses (--resolved, --baseline, --format json) to reproduce a CI failure.

Tokens don't bind to the right property

  • Binding is by type and affinity — fills → background, strokes → border, text → text color, radius → corner. If a value lands on the wrong property, check its type in the token source.
  • Invalid values are rejected before they're applied (a color that isn't hex/rgb/hsl, a dimension without a unit). A rejected value shows a red border in the Tokens table and is never written.

Figma changed but the table didn't update

  • If you have an unsaved edit, auto-sync is held back to avoid clobbering it — a "Figma changed — Refresh" link appears instead. Click it to pull the latest.
  • Auto-sync can be toggled in the Tokens view; it's on by default.

Storybook inserts look wrong

  • Fonts fall back to a system font if the captured font isn't available in the file.
  • Very large captured trees fall back to a type-initial thumbnail; the insert itself still materializes the full node tree.

Org key won't connect

  • The key must be a valid sorb_pk_… organization publishable key from your dashboard.
  • Confirm your account is active and the org exists. See Cloud & accounts.

Plugin sign-in pairing shows "not found"

The plugin's sign-in flow polls a pairing code that a browser tab (where you signed in) is expected to complete. A not_found response means that code is unknown, expired, or already consumed — pairing is single-use, so polling again after a successful connect, or after leaving the browser tab open too long, both land here. Reopen sign-in from the plugin to mint a fresh code rather than retrying the old one.

A write request 403s with "Publishable keys are read-only"

Publishable keys (sorb_pk_…) are read-only by design — see Hosted vs local. POST /verify and POST /tokens/figma on the bridge both reject a publishable key with:

{ "error": "Publishable keys are read-only", "code": "read_only" }

The fix is to use the org's secret key for whichever integration is making that call — a server-side push or CI job, never a browser bundle. (POST /preview itself does not require a secret key: the plugin's sign-in pairing only ever issues a publishable key, so the preview lifecycle deliberately accepts either key type — only /verify and /tokens/figma are write-gated.)

Still stuck?

Reach the team through the support form or email contact@sorbcloud.com. Because this is a beta, please include what you were doing, the plugin view, and any error text.

Works with Figma. Not affiliated with, or endorsed by, Figma. Figma is a trademark of Figma, Inc.