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

On an embedded experience (Flex Inline or Flex SSR), navigating swaps pages in place and replaces the experience element — which silently invalidates the handle you captured. Re-connect on the flex.page.change event and rebind your listeners. Full pattern in Delivery modes.

On a Ceros-hosted page, navigation is a full page load, so your script just runs again.

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

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 throwsonly() 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.