Skip to main content

Troubleshooting

The SDK is built not to break your page: a lookup that matches nothing, or a method a component doesn't have, does nothing and explains itself in the console rather than throwing. So the browser console is the first place to look — every message the SDK writes is prefixed Flex SDK:, Flex Experience SDK:, or [flex-sdk], so searching the console for "flex" finds all of them.

Common problems​

Nothing happens at all​

Work through these in order:

  1. Are you on a published page? The SDK doesn't run in the Flex editor. Open the published URL or a preview link.
  2. Did the script load? Check the Network tab for a request for flex-experience-sdk.js, and the console for a module-resolution error.
  3. Is it a module script? <script type="module"> is required — import and top-level await don't work in a classic script.
  4. Did connect() resolve? Add a console.log after the await. If nothing prints, see connect() never resolves.
  5. Did your lookup match? console.log(experience.findByLocator('x').count). Zero means the locator isn't on the page — see My locator finds nothing.

connect() never resolves​

connect() waits for the experience to be ready, and by default waits forever. Usual causes:

  • You're in the Flex editor. Nothing to connect to. Use a published URL.
  • The root you passed doesn't contain the experience. Check your selector actually matched the right wrapper.
  • The experience never mounted — a lazy-loaded embed that stayed below the fold, or a failed load.

Pass a timeout while you're debugging so you get an error instead of silence:

try {
const experience = await connect(undefined, { timeout: 10_000 })
} catch (error) {
console.error(error) // says what it was waiting for
}

My locator finds nothing​

experience.findByLocator('hero').count // 0
  • Check the spelling against the SDK Locators inspector in Flex. Locators are case-sensitive.
  • Republish. A locator added in Flex only reaches the live page once the experience is published again.
  • Is the element on this page? Lookups only see the page currently on screen. On a multi-page experience, look it up after navigating.
  • One name at a time. findByLocator('cta hero') never matches. Query cta or hero — either finds an element carrying both.
  • Timing. A lookup reads the page at the moment you call it. If you're searching for something revealed later, run the lookup then, or subscribe on a container and use event bubbling.

It works on the first page, then stops​

The page changed without reloading the document, so your script never ran a second time and the experience element it captured has been replaced. Every later lookup and listener is attached to a detached element, which is why nothing throws.

That happens on an embedded experience (Flex Inline or Flex SSR), which always swaps pages in place, and on a standalone experience whose Page navigation setting is on. Re-connect on the flex.page.change event and rebind your listeners — the fix is the same in both, and harmless on a full page load, where the event never fires. Full pattern in Delivery modes.

My video won't play​

media.play() behaves like a viewer tapping play, and browsers block autoplay without a user gesture. Starting playback from inside a component.click handler is the reliable pattern. It's also a no-op before the video has finished rendering — try it after connect() has resolved, not before.

My text change gets ignored or looks wrong​

Use text.setText(). Writing to textContent or innerHTML on the element fights with what Ceros renders and can corrupt the component.

Nothing works inside an iframe on my own page​

Scripts in the parent page can't reach into a cross-origin iframe. If you're embedding via an iframe, the SDK script has to be part of the experience (its custom body HTML), not the surrounding page. To script from your own page, use Flex Inline or Flex SSR instead — see Delivery modes.

What the console messages mean​

Warnings are deduplicated per page load, so a message inside a loop appears once rather than a thousand times.

matched 0 components that support it — the call did nothing​

Your set was empty, or no member supports that method. Check the locator, and whether you're on the right page.

matched 0 components carrying the '<capability>' capability​

The components you found don't have that capability — media.play() on text, for instance. Check component.componentType and component.capabilities to see what you actually matched.

reached N component(s) carrying '<capability>'; skipped M without it​

A mixed set: the call applied where it could and skipped the rest. Usually harmless. To silence it, narrow the set first:

experience
.findByLocator('media-row')
.filter((c) => c.has('media'))
.media.play()

returns a value, so it ran on the FIRST of N matched components​

You called a read — getText, getAttribute, isHidden, states.current — on a set holding more than one component, and only one value can come back. Say which one you meant:

set.first()?.text.getText() // the first, explicitly
set.only().text.getText() // assert there's exactly one
set.forEach((c) => console.log(c.text.getText())) // all of them

.experience read on a set whose members span more than one experience​

You read experience off a set you assembled from searches in two different experiences, and only one can come back — it used the first member's. Read it off a single component instead (set.first()?.experience), or narrow the set first. A set from a single lookup can't hit this.

states.toggle("x") — <component> declares no such state. Available: …​

The state name doesn't exist on that component. The message lists the ones that do; names must match what you called the state in Flex, and are case-sensitive.

pages.goTo("x") — no such page (N pages: …)​

The slug or index doesn't exist. The message lists the available slugs; experience.pages.list() gives you the same thing in code.

findByTag("…") — not a valid selector. Returning 0​

The tag isn't a valid component tag. Tags look like cml-text, cml-video, cml-image.

setText() — <component> exposes no setText() yet​

You called text.setText() on something that isn't a text component. The call was ignored rather than risk corrupting it.

ComponentSet.only() — expected exactly 1 component, found N​

This one throws — only() is the deliberate assertion that exactly one thing matched. Found 0 or 2+? Either the locator is wrong, or it's reused across elements. Use .first() if any match will do, or scope the search to a section first:

experience.findByLocator('pricing').only().findByLocator('cta').only()

setAttribute("cml-id") / setAttribute("editor-…")​

These two throw. cml-id identifies the component and can't be reassigned; editor-* attributes exist only while authoring and never reach a published page. Both are readable — it's only writing them that's blocked. For your own values, use setData.

createStore() persistence load/save failed; continuing in-memory​

Browser storage was unavailable — private browsing, a full quota, or a sandboxed iframe. The store keeps working in memory for the rest of the visit; it just won't remember anything next time.

a store subscriber for "<key>" threw​

Your subscriber callback raised an error. It's caught so it can't break other subscribers, but the error is yours to fix — the message includes it.

[flex-sdk] connect needs an experience root element, got null​

You passed null to connect(), almost always from a querySelector that found nothing. Check the selector. If the element genuinely doesn't exist yet, pass a container that does and let connect wait for the experience inside it.

[flex-sdk] connect timed out after Nms​

No ready experience appeared inside the root you passed before your timeout elapsed. See connect() never resolves.

Still stuck?​

Contact your Ceros representative with:

  • The published URL where it reproduces.
  • The exact console output.
  • Your script, or the part that matters.
  • SDK_VERSION from the page — import { SDK_VERSION } from '@ceros/flex-experience-sdk' — which pins down exactly which bundle you loaded.