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 devis running and note the origin it prints (defaulthttp://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.1and 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:
| Outcome | HTTP status | Cause | Fix |
|---|---|---|---|
not_found | 404 | The 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. |
unauthorized | 401 or 403 | The 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. |
network | any other status, or fetch itself rejected | The 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:
- 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-defaulton<html>, matches no expected prefix, and touches nothing your CSS reads. Check withdocument.documentElement.getAttribute('style')in the console: a value starting----is this cause. Re-post the body without the dashes. - Vocabulary mismatch. Compare the prefixes the plugin is writing against
preview.expectPrefixesin yourSorbConfig— 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'snot_found/networkhandling 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.