Troubleshooting
Real failures, in the order you are likely to meet them.
”Vio hooks must be used inside a <VioProvider>” — from inside a provider
You are rendering two copies of the SDK. It happens when a bundler resolves @vio-live/react-native-sdk/react and .../ui to different copies — a duplicated install, or a monorepo with two node_modules.
npm ls @vio-live/react-native-sdkOne entry, one version. If you see two, dedupe them.
”[Vio] not configured. Call Vio.init(…)”
Something read the configuration before the provider ran. The usual cause is reading Vio.config in the same component that renders <VioProvider> — React builds the children elements first, so init has not happened yet.
// Throws: evaluated while App builds its children
<VioProvider apiKey={KEY}>
<Text>{Vio.config.environment}</Text>
</VioProvider>
// Works: read it in a child
const Readout = () => <Text>{Vio.config.environment}</Text>;Or call Vio.init() at module scope in your entry file, before anything renders.
Every request returns 401
Check the environment. A key issued for staging is not valid on production, and the two fail identically.
console.log(Vio.config.environment, Vio.config.apiBase);Remember the middle environment is testing, not staging.
The catalog throws “Cannot return null for non-nullable field”
The commerce backend returned a product with a null value in a field its schema declares non-nullable, and GraphQL fails the whole query rather than that one field. It is not something the SDK can work around.
If it appears against a channel whose products look fine, try uncached:
await Vio.catalog.getProducts({ currency: 'NOK', useCache: false });If uncached works and cached does not, the backend’s product cache holds an incomplete row — report the channel to Vio support.
A carousel with a locationId renders nothing
By design: a slot with no active placement renders nothing at all. Check, in order:
- Your app declared the slot —
Vio.placements.register({ id: 'home_top' }), at module scope. - The manifest reached the backend — it uploads on the first
bootstrap(). - An operator bound a placement to that slot in the dashboard.
- The campaign is active.
(await Vio.bootstrap()).campaigncarriesisActiveand its dates.
console.log(await Vio.components()); // [] means nothing is boundLive events stop arriving after the app was backgrounded
The operating system closed the socket. The SDK cannot import React Native’s AppState without becoming a React Native dependency, so your app owns that trigger:
useEffect(() => {
const sub = AppState.addEventListener('change', (state) => {
if (state === 'active') Vio.live.reconnect();
});
return () => sub.remove();
}, []);The Android build fails and iOS does not
Almost always the Klarna Maven repository. Gradle only looks in repositories your app declares, and Klarna publishes to its own CDN:
allprojects {
repositories {
maven { url 'https://x.klarnacdn.net/mobile-sdk/' }
}
}See Android specifics.
A payment method does nothing in Expo Go
Native payment SDKs cannot load in Expo Go. You need a development build :
npx expo prebuild && npx expo run:iosEverything else — catalog, cart, components, the checkout form — runs in Expo Go.
Klarna authorises and the backend rejects the token
Klarna required a second step. onAuthorized arrives with finalizeRequired and a null token; the usable one comes later through onFinalized. VioKlarnaView handles this; if you drive Klarna’s SDK yourself, you must.
Vipps opens a web page instead of the app
The URL was opened in a WebView, or modified. Open it with Linking.openURL exactly as returned — the app switch only happens when the operating system handles it.
Prices are grouped differently on Android
Android does not ship the full ICU data, so toLocaleString falls back. The SDK’s formatPrice groups by hand for that reason; if you format prices yourself, expect the difference and test on a device.
The theme font applies on one platform only
iOS resolves a font by PostScript name, Android by file name. They are often different strings for the same file.
Turn on debug in Vio.init({ debug: true }) and the SDK logs its resolved configuration — the
masked key, both base URLs, the socket host and the subscribed modules. That one line answers most
“which backend am I even talking to” questions.