Summary
Enterprise customers behind corporate VPNs or strict firewall policies frequently fail to connect to the avatar service — often silently, with no actionable feedback. This issue documents the root cause, the real-world impact observed, and a fully-designed SDK-level feature to detect the block early and fall back gracefully to a pre-recorded VOD avatar.
Background: What Is Happening Today
The failure mode
When a user on a corporate VPN or behind a network proxy visits an app using this SDK, the following sequence occurs:
- SDK loads and calls
sdk.connect()
- Socket.IO begins with XHR long-polling as its first transport to
https://conversation.avatar.us.kaltura.ai/socket.io/?client=...
- The corporate SSL-inspection proxy intercepts the HTTPS connection, re-terminates TLS, and proxies the request — but strips the
Access-Control-Allow-Origin response header before it reaches the browser
- The browser enforces the missing CORS header and blocks the request
- Socket.IO never upgrades to WebSocket; Error 1001 fires immediately
- The SDK emits a generic
connection-error event with [KalturaAvatar] ERROR: Connection error — xhr poll error
- The app shows a generic "Error loading avatar" spinner
Console errors observed by the customer:
Access to XMLHttpRequest at 'https://conversation.avatar.us.kaltura.ai/socket.io/?client=115767973963...'
from origin 'https://cdnapi-ev.kaltura.com' has been blocked by CORS policy:
No 'Access-Control-Allow-Origin' header is present on the requested resource.
Failed to load resource: net::ERR_FAILED conversation.avatar...polling&t=PxqPKZY:1
[KalturaAvatar] ERROR: Connection error server error
[KalturaAvatar] ERROR: Connection error xhr poll error
[Avatar] Error: 1001 xhr poll error
A second, distinct failure mode (concurrent session)
Separately, when the avatar flow has a single-session concurrency limit: if another session already holds the flow, a second user gets Socket.IO signaling connected ("avatar connected") but WHEP media delivers zero RTP packets — resulting in a black/silent avatar with no error message at all. The user has no way to know whether to wait, refresh, or give up.
Who Is Affected
- Any enterprise customer whose IT department runs a next-gen firewall, SSL inspection proxy (Zscaler, Palo Alto, Cisco Umbrella, Netskope, etc.), or zero-trust network gateway
- This is a majority of large enterprise accounts — the exact customer segment this SDK is designed to serve
- Customers working remotely via corporate VPN are particularly affected, as VPN split-tunneling decisions are made at the IT level without end-user control
Why This Is an SDK Problem, Not an App Problem
The SDK has full knowledge of:
- The target WebSocket endpoint it needs to reach
- The connection state and failure mode
- The transport sequence (XHR poll → WebSocket upgrade)
Applications built on top of the SDK have none of this knowledge and cannot reliably distinguish a CORS-blocked network probe from a server error, a transient failure, or a misconfiguration. The correct place to implement detection and graceful degradation is inside the SDK, where it can be done once and benefit all integrations.
Proposed Solution
Part 1: Pre-flight connectivity probe
Before initiating the Socket.IO connection, the SDK runs a lightweight HTTP HEAD request to the avatar service's /health endpoint with a short timeout:
async function probeConnectivity(baseUrl, timeoutMs = 4000) {
const controller = new AbortController();
const timer = setTimeout(() => controller.abort(), timeoutMs);
try {
await fetch(baseUrl + '/health', {
method: 'HEAD',
signal: controller.signal,
mode: 'cors'
});
return { blocked: false };
} catch (e) {
return {
blocked: true,
reason: e.name === 'AbortError' ? 'timeout' : 'cors-or-network'
};
} finally {
clearTimeout(timer);
}
}
Requirements on the server side: The /health endpoint must return HTTP 200 with correct Access-Control-Allow-Origin headers. This endpoint also acts as the canonical connectivity signal — if it is reachable, the full Socket.IO connection will be too.
If the probe fails, the SDK:
- Emits a new
connection-blocked event (see API below) instead of attempting the doomed full connection
- Does not retry (retrying a CORS-blocked endpoint is pointless and wastes time)
- Optionally renders a VOD fallback if configured (see Part 2)
Part 2: VOD avatar fallback rendering
When a network block is detected, the SDK can render a pre-recorded avatar video in place of the live avatar, explaining the issue and providing IT resolution steps.
New constructor option:
interface NetworkFallbackConfig {
/**
* 'vod-entry' — customer supplies their own Kaltura entry ID
* 'kaltura-default' — SDK renders a standard Kaltura-hosted fallback video
* for the given persona name
* 'none' — SDK only emits the event; app handles rendering
*/
mode: 'vod-entry' | 'kaltura-default' | 'none';
/** For mode='vod-entry': Kaltura entry ID of the customer's fallback video */
entryId?: string;
/** For mode='kaltura-default': avatar persona name to use for the fallback video */
persona?: string;
/** Kaltura partner ID (required for 'vod-entry' and 'kaltura-default') */
partnerId?: number;
/** URL to an IT configuration guide. Shown as overlay or burned into video.
* Defaults to Kaltura's official requirements page if omitted. */
itLink?: string;
}
new KalturaAvatarSDK({
clientId: '...',
flowId: '...',
container: '#avatar-pip',
networkFallback: {
mode: 'kaltura-default',
persona: 'sophia',
itLink: 'https://knowledge.kaltura.com/help/avatars-network-requirements-and-firewall-configuration'
}
});
New event:
sdk.on('connection-blocked', (event: {
reason: 'cors' | 'timeout' | 'network-error';
itLink: string;
probeUrl: string;
}) => {
// Application can render its own UI when mode='none' or augment the built-in fallback
});
Behavior by mode:
| Mode |
What SDK renders |
Customer effort |
'kaltura-default' |
Standard Kaltura-branded fallback video for the named persona |
Zero — just set persona name |
'vod-entry' |
Customer's own branded fallback video via Kaltura Player |
Customer pre-generates one video using the VOD Avatar Studio API |
'none' |
Nothing — emits connection-blocked event only |
App builds custom fallback UI |
Part 3: Infrastructure — pre-generated fallback video library
For mode: 'kaltura-default' to work, Kaltura needs to produce a set of standard fallback videos using the VOD Avatar Studio API, one per popular persona. Suggested script:
"Hi — it looks like your network is blocking the live avatar connection. To fix this, please ask your IT team to allow the Kaltura avatar domains. You can find the complete list of requirements at the link shown below. We look forward to connecting with you soon."
These videos would be hosted on Kaltura CDN and referenced in a lookup table inside the SDK bundle:
const FALLBACK_ENTRY_IDS = {
sophia: '1_xxxxxxx',
ron: '1_yyyyyyy',
james: '1_zzzzzzz',
// ... remaining personas
};
Suggested priority order for first batch: the 10 most commonly used personas across active deployments.
Part 4: Concurrent-session detection (separate but related)
When sdk.connect() succeeds at the signaling level but WHEP media delivers zero RTP after a configurable timeout (suggested: 5 seconds), emit a distinct media-unavailable event rather than silently showing a black avatar:
sdk.on('media-unavailable', (event: {
reason: 'no-rtp' | 'track-muted';
sessionId: string;
}) => { });
Built-in message suggestion when this event fires: "The avatar is currently in use — please try refreshing in a moment."
Rollout / Who Does What
| Task |
Owner |
Notes |
Add /health CORS endpoint |
Backend / infra |
Prerequisite for probe |
| Implement pre-flight probe in SDK |
SDK team |
probeConnectivity() + connection-blocked event |
Add networkFallback config + VOD renderer |
SDK team |
Uses existing Kaltura Player embed |
| Pre-generate fallback video library |
Kaltura content / VOD Avatar Studio |
One-time batch job via VOD Avatar API |
Add media-unavailable event for black-video case |
SDK team |
Track muted state on WHEP stream |
| Update SDK documentation |
SDK team |
Document new config options and events |
Reference: Network Requirements
The official Kaltura avatar network requirements page that the fallback should link to:
https://knowledge.kaltura.com/help/avatars-network-requirements-and-firewall-configuration
This URL should be the default value of itLink when the customer does not supply their own.
Acceptance Criteria
Priority
High — this blocks enterprise sales. The affected customer profile (large enterprises on VPN) is the primary buyer of Kaltura Avatar deployments. The feature requires no breaking changes and is fully backwards-compatible.
Summary
Enterprise customers behind corporate VPNs or strict firewall policies frequently fail to connect to the avatar service — often silently, with no actionable feedback. This issue documents the root cause, the real-world impact observed, and a fully-designed SDK-level feature to detect the block early and fall back gracefully to a pre-recorded VOD avatar.
Background: What Is Happening Today
The failure mode
When a user on a corporate VPN or behind a network proxy visits an app using this SDK, the following sequence occurs:
sdk.connect()https://conversation.avatar.us.kaltura.ai/socket.io/?client=...Access-Control-Allow-Originresponse header before it reaches the browserconnection-errorevent with[KalturaAvatar] ERROR: Connection error — xhr poll errorConsole errors observed by the customer:
A second, distinct failure mode (concurrent session)
Separately, when the avatar flow has a single-session concurrency limit: if another session already holds the flow, a second user gets Socket.IO signaling connected (
"avatar connected") but WHEP media delivers zero RTP packets — resulting in a black/silent avatar with no error message at all. The user has no way to know whether to wait, refresh, or give up.Who Is Affected
Why This Is an SDK Problem, Not an App Problem
The SDK has full knowledge of:
Applications built on top of the SDK have none of this knowledge and cannot reliably distinguish a CORS-blocked network probe from a server error, a transient failure, or a misconfiguration. The correct place to implement detection and graceful degradation is inside the SDK, where it can be done once and benefit all integrations.
Proposed Solution
Part 1: Pre-flight connectivity probe
Before initiating the Socket.IO connection, the SDK runs a lightweight HTTP HEAD request to the avatar service's
/healthendpoint with a short timeout:Requirements on the server side: The
/healthendpoint must return HTTP 200 with correctAccess-Control-Allow-Originheaders. This endpoint also acts as the canonical connectivity signal — if it is reachable, the full Socket.IO connection will be too.If the probe fails, the SDK:
connection-blockedevent (see API below) instead of attempting the doomed full connectionPart 2: VOD avatar fallback rendering
When a network block is detected, the SDK can render a pre-recorded avatar video in place of the live avatar, explaining the issue and providing IT resolution steps.
New constructor option:
New event:
Behavior by mode:
'kaltura-default'personaname'vod-entry''none'connection-blockedevent onlyPart 3: Infrastructure — pre-generated fallback video library
For
mode: 'kaltura-default'to work, Kaltura needs to produce a set of standard fallback videos using the VOD Avatar Studio API, one per popular persona. Suggested script:These videos would be hosted on Kaltura CDN and referenced in a lookup table inside the SDK bundle:
Suggested priority order for first batch: the 10 most commonly used personas across active deployments.
Part 4: Concurrent-session detection (separate but related)
When
sdk.connect()succeeds at the signaling level but WHEP media delivers zero RTP after a configurable timeout (suggested: 5 seconds), emit a distinctmedia-unavailableevent rather than silently showing a black avatar:Built-in message suggestion when this event fires: "The avatar is currently in use — please try refreshing in a moment."
Rollout / Who Does What
/healthCORS endpointprobeConnectivity()+connection-blockedeventnetworkFallbackconfig + VOD renderermedia-unavailableevent for black-video casemutedstate on WHEP streamReference: Network Requirements
The official Kaltura avatar network requirements page that the fallback should link to:
https://knowledge.kaltura.com/help/avatars-network-requirements-and-firewall-configuration
This URL should be the default value of
itLinkwhen the customer does not supply their own.Acceptance Criteria
probeConnectivity()runs beforesdk.connect()and completes in ≤ 4 secondsconnection-blockedevent emission; full connection attempt is skippedmode: 'kaltura-default'renders a Kaltura Player embed with the pre-generated persona video in the SDK containermode: 'vod-entry'renders the customer-supplied entry in the SDK containermode: 'none'emits only the event with no DOM changesitLinkis surfaced as a clickable overlay or caption on the fallback videomedia-unavailableevent fires when signaling succeeds but no RTP received within 5sPriority
High — this blocks enterprise sales. The affected customer profile (large enterprises on VPN) is the primary buyer of Kaltura Avatar deployments. The feature requires no breaking changes and is fully backwards-compatible.