The entry point for the HappyOyster Android SDK is the singleton object HappyOyster . Except for initialize , updateToken , attachVideo , and sendCommand , all business methods are suspend functions that throw SDKError on failure. Covers the three modes adventure / directing / acting, the required model config, and aspectRatio.
If the caller coroutine is cancelled, the SDK cancels that call's in-flight HTTP request, if any, and propagates CancellationException unchanged rather than converting it to SDKError. Cancellation does not roll back a request that the server has already accepted.
For the integration flow, installation, and best practices, see Happy Oyster Android SDK Integration Guide.
Core Terminology
Term | Meaning |
|---|---|
token | Bailian gateway API Key: injected as a Bearer token by your app via |
ticket | One-time experience credential: exchanged by your server via the open platform and delivered to the client, used only for a single |
Environment Requirements
Item | Requirement |
|---|---|
minSdk | 24 (Android 7.0) and above |
compileSdk | 36 |
Language | Kotlin (coroutine |
ABI |
|
Network | Public internet access required |
Core Concepts
Concept | Description |
|---|---|
World | An AI world, containing characters and scenes. Created and managed by your server. |
Travel | A single real-time experience. Basic lifecycle: |
Mode |
|
Real-time video | Established and maintained automatically by the SDK after |
Overview
HappyOyster methods
Method | Description |
|---|---|
| Initializes the SDK. Idle re-initialization is supported; re-initialization while a Travel is starting, active, or ending is rejected with |
| Injects/updates the Bailian gateway API Key (Bearer token). |
| Starts an experience with a one-time credential and automatically establishes the real-time video connection. |
| Asynchronously pauses the experience (directing and Acting, and only if the experience supports pausing); after acceptance, the actual pause is determined by |
| Resumes a paused experience (directing and Acting); includes 3× retry backoff internally. |
| Rewinds to the specified number of seconds (directing only, state |
| Sends a text instruction to drive the narrative (directing and Acting, |
| Sends direction/view/action control commands (adventure mode, |
| Returns a |
| Ends the experience, automatically disconnecting the real-time connection and releasing all session resources. |
| SDK version string (SemVer), a compile-time constant. |
Events
Event | Description |
|---|---|
| Experience status change (including the real-time video lifecycle); values are defined in |
| Callback when an internal automatic flow fails; a fatal error also terminates the current experience. |
Key data types
Type | Description |
|---|---|
| SDK initialization configuration ( |
| Experience status: |
| Experience mode: |
| World creation model: |
| Returned by |
| Parameter for |
| SDK error; contains |
HappyOyster.initialize
Initializes the SDK; call it before any other API (preferably in Application.onCreate). config is required and must carry your account's Bailian apiHost and a model of happyoyster-1.0-directing / happyoyster-1.0-acting / happyoyster-1.0-adventure (matching the Open API entry). The SDK constructs request URLs as follows:
https://{apiHost}/api/v2/apps/{model}/openapi/v1/{endpoint}
model has no default value: omitting it when constructing SDKConfig is a compile-time error. Passing a blank value makes initialize synchronously throw SDKError(100002); no runtime is created or replaced and no network request is made. The SDK holds only the application context, not an Activity. Re-initialization is supported only while the previous runtime is idle. To switch models, re-initialize with the new model while idle; if a Travel is starting, active, or ending, await endTravel() first.
Note
Regional compliance notice:- To support compliance with applicable legal and data requirements, if your intended users include U.S. users, you must configure a U.S.-region API Host during SDK initialization for services provided to those users.
- Developers are responsible for correct configuration and bear the corresponding responsibility under applicable law for failure to follow this requirement.
Signature
fun initialize(context: Context, config: SDKConfig)
Parameters
Field | Type | Required | Description |
|---|---|---|---|
|
| Yes | Passing |
|
| Yes | SDK configuration; must carry the required, default-free |
Returns
No return value.
Errors
code | Description |
|---|---|
|
|
| Re-initialization rejected because a Travel is starting, active, or ending. Await |
HappyOyster.updateToken
Injects/updates the Bailian gateway API Key. Thread-safe and callable after initialize; the SDK uses the latest token for startTravel and subsequent experience-control requests. The ticket is a one-time experience credential and should not be mixed up with the Bearer token.
Signature
fun updateToken(token: String)
Parameters
Field | Type | Required | Description |
|---|---|---|---|
|
| Yes | The Bailian gateway API Key, used as the Bearer token. |
Returns
No return value.
Errors
code | Description |
|---|---|
| SDK has not been initialized. |
HappyOyster.startTravel
Starts an experience with a one-time ticket. On success, the SDK automatically establishes the real-time video connection and begins internal status polling, with status surfaced via onStatusChanged.
ticketis a one-time credential and is considered consumed once the call is made.- Only one concurrent Travel is allowed per app at any given moment; calling again while an experience is in progress throws
SDKError(103004), withSDKError.rawcontaining only a redacted summary of the currently active ticket for diagnosis, never the full ticket. - Optionally pass
maxExperienceTimeSecto cap the maximum duration of this experience (world-exploration / adventure mode only); see Parameters.
StartTravelData.creationModel (CreationModelValue, default CreationModelValue.Simple): indicates the world's creation model, i.e., how script content is managed. simple (default) is an ordinary instruct-driven world; world-exploration worlds are also normalized to this value. scriptlist is a structured ScriptList world—its script content is not accessed through this SDK. In ScriptList mode (creationModel == CreationModelValue.ScriptList), calling sendInstruct is rejected and throws SDKError(103002). This field defaults to simple.
StartTravelData.aspectRatio (String?): the playback aspect ratio the server assigned to this session, as a width:height string. Non-null only for Acting—"9:16" (portrait, the server-side default at creation) or "16:9" (landscape); null for world-exploration and directing modes, and null when the server does not report the value. The SDK preserves unknown values verbatim (it does not collapse them to null), so hosts must treat any unrecognized value like null and fall back to their own default orientation; the SDK itself does not consume this field.
The value is delivered with the startTravel() return value: size the playback container from it after startTravel() returns and before calling attachVideo() and adding the returned view to your layout—the SDK only starts binding the remote stream for rendering once the host attaches the view, so deciding the orientation at that point still precedes the first rendered frame (it is, however, not guaranteed to happen before the SDK joins the real-time room). The remote view is bound with a clip-to-fill render mode, so a container whose orientation disagrees with this value crops the picture instead of letterboxing it.
Signature
suspend fun startTravel(ticket: String): StartTravelData
suspend fun startTravel(ticket: String, maxExperienceTimeSec: Int?): StartTravelData
Parameters
Field | Type | Required | Description |
|---|---|---|---|
|
| Yes | The one-time experience credential, delivered by your server. |
|
| No | Maximum duration of this experience in seconds; the world ends the session automatically once it is reached. Applies to world-exploration (adventure) mode only; ignored in directing (real-time directing) and Acting modes. The allowed values are server-configured, currently |
Returns
Returns StartTravelData, containing experience metadata (mode, version, creationModel, aspectRatio, etc.). Inspect the returned metadata before deciding how to interact (e.g., travel.mode, travel.creationModel, travel.version); for Acting, also size the playback container from travel.aspectRatio.
Errors
code | Description |
|---|---|
|
|
|
|
| Invalid parameter (e.g. |
| World not in a ready state |
| Resource allocation / internal service failure |
| A Travel is already starting, active, or ending, or |
HappyOyster.pauseTravel / HappyOyster.resumeTravel
Pause / resume the experience. The SDK manages only one Travel at a time; encryptedTravelId is obtained internally by the SDK and need not be passed by the caller.
Preconditions:
pauseTravel: callable only in directing or Acting and when the state isrunning.resumeTravel: callable only in directing or Acting and when the state ispaused.
Not all experiences support pause / resume: the experience must report the version identifier its mode requires (StartTravelData.version—storyV2 for directing, actingV2 for Acting; compared ignoring surrounding whitespace and letter case), otherwise both pauseTravel and resumeTravel return 103002. Acting supports pause / resume but does not support rewindTravel.
Pause is asynchronous (important): pauseTravel is a "heavy" operation—a successful call (method return) only means the pause has been accepted; at that moment the experience is not actually paused yet. The pause is only real once the onStatusChanged callback reports paused. Therefore, drive the host state machine and call gating from that callback: between "calling pauseTravel" and "receiving the paused callback" you may mark the local state as pausing; only after receiving paused should you allow initiating resumeTravel or rewindTravel. Do not treat the return of pauseTravel as already paused.
Handling of the real-time connection: after an actual pause, the SDK disconnects the real-time connection; on resume, the SDK automatically rejoins and restores the picture using the credential delivered during the same startTravel, with no host intervention required.
Ordered pause-teardown barrier: after Paused is emitted, server-side real-time-room teardown may still lag briefly. The SDK establishes a 3-second settle window from pause confirmation. If resumeTravel or rewindTravel is called inside that window, the suspending call first waits non-blockingly for the remaining time, then sends the room-reopening API request. No delay is added if 3 seconds have already elapsed naturally. This prevents a late pause teardown from closing the newly opened room and surfacing 105001.
resume API retry: resumeTravel internally retries up to 3 times on failure (backoff 1 s / 2 s / 3 s) to handle brief service unavailability after a pause; only if all 3 attempts fail is the error propagated upward.
Note⚠️ Note: It is recommended that the host wait up to 3 s after resumeTravel returns successfully before initiating pauseTravel again, to avoid switching too frequently. This is a host-side call cooldown recommendation and is not enforced by the SDK itself. See Happy Oyster Android SDK Integration Guide for details.
Signature
suspend fun pauseTravel(): TravelStateData
suspend fun resumeTravel(): TravelStateData
Parameters
No parameters.
Returns
Returns TravelStateData (contains encryptedTravelId, status).
Errors
code | Description |
|---|---|
| No active experience |
| State not allowed, or this experience does not support pause / resume |
| Called in adventure mode; only directing and Acting support pause / resume |
HappyOyster.rewindTravel
Rewinds to the specified number of seconds. encryptedTravelId is obtained internally by the SDK.
Preconditions: callable only in directing mode and when the state is paused (rewinding is not allowed in the running state). Not all directing experiences support rewinding, and unsupported ones return 103002. After a successful rewind, the experience resumes automatically and the SDK automatically reconnects RTC, with no host intervention required.
Acting has no rewind capability at all: calling it in Acting is rejected locally by the SDK with 103003, and no request is issued. In Acting, hosts should hide the rewind entry point rather than merely disabling the button.
Signature
suspend fun rewindTravel(rewindToSec: Double): RewindTravelData
Parameters
Field | Type | Required | Description |
|---|---|---|---|
|
| Yes | The target number of seconds to rewind to, which should be a multiple of 4 (e.g., 4, 8, 12). A non-multiple of 4 is floored by the server down to the nearest smaller multiple of 4 (e.g., passing 7 yields 4). |
Returns
Returns RewindTravelData (contains encryptedTravelId, status, resumedAtSec), where resumedAtSec is the number of seconds the server actually rewound to (already floored to a multiple of 4).
Errors
code | Description |
|---|---|
| No active experience |
| State is not |
| Called in adventure or Acting mode; only directing mode supports rewinding |
HappyOyster.sendInstruct
Sends a text instruction to drive the narrative. Valid in directing or Acting and when the experience state is running or paused. encryptedTravelId is obtained internally by the SDK.
Behavior in the paused state: the SDK does not automatically resume while paused; the instruct is sent directly. It is up to the host to decide whether to call resumeTravel first before sending the instruct.
Signature
suspend fun sendInstruct(content: String): SendInstructData
Parameters
Field | Type | Required | Description |
|---|---|---|---|
|
| Yes | The text instruction content to send. |
Returns
Returns SendInstructData (contains encryptedTravelId, content, accepted).
Errors
code | Description |
|---|---|
| No active experience (never called |
| Current mode is neither directing nor Acting (called in adventure mode) |
| The experience state is neither |
| Content moderation block |
| Travel does not exist |
HappyOyster.sendCommand
Sends direction/view/action control commands. Valid only in adventure mode and when running.
- Call on the main thread; an off-main call synchronously throws
IllegalStateException. - No active experience reports
103001. - Calling outside adventure mode (directing / Acting) reports
103003. - A disallowed state reports
103002. - The uplink is established via a silent audio stream (the SDK does not record or upload real audio);
RECORD_AUDIOis not a hard prerequisite for the DataChannel, but declaring and granting it is recommended for cross-device compatibility (see Happy Oyster Android SDK Integration Guide § Installation · Permissions).105004means the real-time channel is not ready / a send failed (e.g. a DataChannel connection interruption or a real-time channel anomaly); it is not triggered directly by a missing permission.
Built-in 42 ms throttling (24 fps, latest-wins): sendCommand internally throttles DataChannel writes in 42 ms intervals (about 24 fps). State validation runs synchronously and immediately, throwing immediately on an illegal call; the actual DataChannel write is throttled asynchronously. If at least 42 ms has passed since the previous send, the command is dispatched immediately (including the first call). Otherwise, the pending command is replaced with the latest value and dispatched once at the end of the current 42 ms window. Multiple calls in one window therefore produce one on-wire write containing the last value. The host may call sendCommand at the game frame rate without implementing its own rate limiter.
Send failures during throttling: the throttled flush is asynchronous, and a send failure cannot be thrown to the caller—the error is surfaced via the onError callback (non-fatal, 105004). Pending commands are discarded when the session ends (they will not be sent after the session ends).
AdventureCommand fields and values:
Field | Semantics | Values |
|---|---|---|
| Movement: forward/left/back/right/diagonal/idle |
|
| View: up/down/left/right/diagonal/none |
|
| Interaction: jump/attack/squat/sprint/none |
|
The three fields are independent mutually exclusive command groups. Diagonal movement/view uses a single combined value (for example, forward+left is W_A, not concurrent W and A in the same field). Each call should carry the complete current state.
Use the 42 ms interval as the mental model:
-
One-shot action (for example, tap jump/attack or take one step): call once. The SDK sends it in the nearest interval; no repeated calls or follow-up
Nonecommand is required.// Jump once HappyOyster.sendCommand(AdventureCommand("None", "None", "Jump")) -
Held action (for example, keep moving or rotating): call every frame while held. The SDK emits approximately one command every 42 ms. On release, explicitly send one command containing
Noneto reset the state. The SDK never generates a reset command automatically.// While held: call from the host's frame loop HappyOyster.sendCommand(AdventureCommand("W", "None", "None")) // On release: explicitly reset once HappyOyster.sendCommand(AdventureCommand("None", "None", "None"))
Signature
fun sendCommand(command: AdventureCommand)
Parameters
Field | Type | Required | Description |
|---|---|---|---|
|
| Yes | A command object containing the three fields |
Returns
No return value (synchronous). State validation runs synchronously and immediately; the DataChannel write is throttled asynchronously.
Errors
code | Description |
|---|---|
| No active experience |
| State not allowed |
| Called outside adventure mode (directing / Acting) |
| Throttled flush send failure (non-fatal, asynchronous callback) |
HappyOyster.attachVideo
Returns a SurfaceView for playback, which you add to your layout; the SDK internally completes the render binding with the remote stream.
- Call on the main thread; an off-main call synchronously throws
IllegalStateException. - The SDK holds only a weak reference to the returned View and releases the render binding when the experience ends; you must remove the View from the layout yourself.
- For Acting experiences, size the playback container from
StartTravelData.aspectRatiobefore adding the returned view to your layout: rendering is bound with a clip-to-fill mode, so a mismatched orientation crops the picture (seestartTravel).
Signature
fun attachVideo(): SurfaceView
Parameters
No parameters.
Returns
Returns a SurfaceView; add it to your layout to play the real-time video.
Errors
code | Description |
|---|---|
| SDK not initialized |
HappyOyster.endTravel
Ends the experience. encryptedTravelId is obtained internally by the SDK. After a successful call (or an abnormal exit), the SDK automatically disconnects the real-time connection, stops internal polling, and releases all session resources, and the current ticket is invalidated at the same time.
Signature
suspend fun endTravel(): EndTravelData
Parameters
No parameters.
Returns
Returns EndTravelData (contains encryptedTravelId, status, endedAt, durationSec).
Errors
code | Description |
|---|---|
| SDK not initialized |
| No active experience |
HappyOyster.VERSION
Returns the SDK version string (SemVer), such as "x.y.z". This value is a compile-time constant injected by the VERSION_NAME Gradle property; it can be read safely without calling initialize first.
Signature
val VERSION: String
Returns
The SDK version string, such as "x.y.z".
Log.d("MyApp", "SDK version: ${HappyOyster.VERSION}")
Event Listening
interface HappyOysterListener {
fun onStatusChanged(status: TravelStatusValue) {}
fun onError(error: SDKError) {}
}
fun addListener(listener: HappyOysterListener)
fun removeListener(listener: HappyOysterListener)
SDK events are a proactive push channel to you, used to report situations not triggered by your own explicit calls (e.g., problems in the real-time connection or status polling that the SDK maintains automatically).
Event | Description |
|---|---|
| Experience status change (including the real-time video lifecycle); values are defined in |
| Callback when an internal automatic flow fails; a fatal error also terminates the current experience (see the error-code section). |
Note⚠️ Note: addListener / removeListener must be called after initialize; calling them before initialization throws SDKError(100001). It is recommended to register listeners immediately after HappyOyster.initialize(...) returns successfully.
Data Models
// Configuration
data class SDKConfig(
// Required: your account's Bailian API Host (e.g., llm-xxxx.ap-southeast-1.maas.aliyuncs.com), copied from the API Key page in the Bailian console;
// must belong to the same account/region as the injected API Key, otherwise the gateway returns AccessDenied.
val apiHost: String,
// Required with no default: the complete HappyOyster model name and version (e.g., happyoyster-1.0);
// refer to the official Bailian HappyOyster model documentation for available values.
val model: String,
// Minimum level for the built-in Logcat sink (does not affect logHandler); defaults to INFO, which
// includes the lifecycle anchors (initialize, Travel start/status/end, RTC connect/first-frame) needed
// to reconstruct a session timeline. Drop to WARN for errors-only, or raise to DEBUG/VERBOSE to diagnose.
val logLevel: LogLevel = LogLevel.INFO,
// Timeout for RTC join (timeout triggers 105002), first-frame wait (timeout triggers 105003), and SDK gateway HTTP signaling calls (timeout triggers 105005);
// the two RTC phases are serial, so the worst-case wait is 2×callbackTimeoutMs (60s).
val callbackTimeoutMs: Long = SDKConfig.DEFAULT_CALLBACK_TIMEOUT_MS, // 30_000ms
// Whether the SDK writes its own logs to Android Logcat (tag HappyOysterSDK); defaults to false (silent).
val logcatEnabled: Boolean = false,
// Host log callback; receives the full stream of SDK LogRecords, unaffected by logLevel; defaults to null.
val logHandler: HappyOysterLogHandler? = null,
) {
companion object {
/** Default callback timeout (milliseconds). Public constant, usable for comparison or display. */
const val DEFAULT_CALLBACK_TIMEOUT_MS: Long = 30_000
}
}
enum class LogLevel { VERBOSE, DEBUG, INFO, WARN, ERROR, NONE }
// Host log sink; injected via SDKConfig.logHandler; full firehose, unaffected by logLevel.
fun interface HappyOysterLogHandler {
fun onLog(record: LogRecord)
}
// SDK structured log record; delivered to HappyOysterLogHandler; contains no sensitive values (Bearer /
// ticket / RTC token, RTC identity fields, and media URLs are all redacted). travelId (encryptedTravelId)
// is kept in full so it can be correlated with server-side session logs.
data class LogRecord(
val level: LogLevel,
val tag: String, // fixed as "HappyOysterSDK"
val message: String, // readable log line (includes event name and details)
val throwable: Throwable?,
val timestampMs: Long, // epoch milliseconds at emission time
)
// Status and mode (unknown values are preserved to allow service extension)
@JvmInline value class TravelStatusValue(val rawValue: String) {
companion object {
val Init = TravelStatusValue("init")
val Pending = TravelStatusValue("pending")
val Running = TravelStatusValue("running")
val Paused = TravelStatusValue("paused")
val Failed = TravelStatusValue("failed")
val Completed = TravelStatusValue("completed")
}
}
@JvmInline value class ModeValue(val rawValue: String) {
companion object {
val Adventure = ModeValue("adventure")
val Directing = ModeValue("directing")
val Acting = ModeValue("acting")
}
}
@JvmInline value class CreationModelValue(val rawValue: String) {
companion object {
val Simple = CreationModelValue("simple") // default; prompt/instruct-driven, world-exploration worlds normalize to this
val ScriptList = CreationModelValue("scriptlist") // structured ScriptList world; sendInstruct not supported (throws 103002)
}
}
// startTravel return
data class StartTravelData(
val encryptedTravelId: String, // identifier for subsequent control interfaces
val encryptedWorldId: String,
val mode: ModeValue, // adventure / directing / acting
val creationModel: CreationModelValue = CreationModelValue.Simple, // world creation model; Simple (default) is instruct-driven, ScriptList does not support sendInstruct
val playUrl: String?,
val firstFrame: String?, // first-frame image address, may be produced asynchronously
val bgmUrl: String?,
val version: String, // world version identifier; Acting is actingV2
val aspectRatio: String? = null, // Acting only: 9:16 / 16:9; other modes null. Use to set player orientation
val maxExperienceTimeSec: Int? = null, // server-reported max experience time (seconds); non-null only for adventure; null for directing / acting
)
// Control interface parameters and returns
data class AdventureCommand(
val translation: String, // movement: forward/left/back/right/diagonal (W_A, …)/idle
val rotation: String, // view: up/down/left/right/diagonal (Mouse_Up_Left, …)/none
val interaction: String, // interaction: jump/attack/squat/sprint/none
)
data class TravelStateData(val encryptedTravelId: String, val status: TravelStatusValue)
data class RewindTravelData(val encryptedTravelId: String, val status: TravelStatusValue, val resumedAtSec: Double)
data class EndTravelData(val encryptedTravelId: String, val status: TravelStatusValue, val endedAt: String, val durationSec: Int)
data class SendInstructData(val encryptedTravelId: String, val content: String, val accepted: Boolean)
// Error (identified by code; the original information is in raw)
// Note: SDKError is not a data class (no copy()/destructuring), it is a plain class.
class SDKError(val code: Int, val raw: Any? = null) : Exception("Happy Oyster SDK error: $code")
Error Codes
Errors are identified by a numeric code. The SDK passes through business error codes that the caller can handle (commonly 4xxxxx / 5xxxxx); local SDK error codes are 1xxxxx.
Business error codes (common)
code | Meaning | Suggested handling |
|---|---|---|
| Invalid parameter (invalid enum, etc.) | Check the request parameters or the SDK version |
|
| Have your server re-issue a credential |
|
| The credential is one-time; re-issue it |
| World does not exist, has been deleted, or does not belong to the current developer (including a world already deleted within the | Reselect a valid World |
| World not in a ready state | Wait until the world is ready before starting |
| Input content violation (content moderation); applies to | Modify the input content and retry |
| Service specification not enabled for this account | Do not retry as capacity-full; switch to an enabled spec or request enablement |
| Capacity configuration temporarily unavailable | Retry later |
| Travel resource does not exist or the ID does not belong to the current account | Restart the Travel |
| Request conflicts with the current resource state | Check the Travel state |
| SKU concurrency limit reached | Retry after an existing session ends (do not confuse with |
| No capacity currently available | Retry later |
| Inference resource allocation / internal service failure | Retry later |
| Internal system error | Retry later / report |
Client-side local error codes
code | Meaning | Fatal? | Suggested handling |
|---|---|---|---|
| Called before the SDK was initialized | Call rejected |
|
|
| Initialization failed | Pass a non-blank complete model name and version, then call |
| Bailian gateway API Key not injected | No | Retry after |
| Bailian gateway Bearer API Key expired or rejected. Note: this is the gateway Bearer Key, not the one-time ticket; ticket-level credential errors are identified by six-digit server codes (e.g., | No | Re-obtain the Bearer Key and |
| No active experience currently | Call rejected |
|
| The current state does not allow this operation (including: state mismatch; the experience does not support pause / resume / rewind for | Call rejected | Check the experience state and mode; |
| Mode mismatch: | Call rejected | Check whether the current mode matches the interface's requirement |
| A concurrent | Call rejected | Await |
| Real-time connection failed | Yes | End and restart |
| Real-time join timed out | Yes | End and restart |
| Timed out waiting for the video first frame | Yes | End and restart |
| Real-time channel not ready / send failed | No | Confirm the real-time channel is ready and retry |
| An SDK gateway HTTP signaling call did not return within | No | Retrying is recommended, or increase |
| Auto-end on no stream: the first frame never arrived, or the stream was interrupted during running and did not recover before timeout. The SDK proactively ends the current experience | Yes | End and restart |
| Local network error | No | Retryable |
| Response parsing failed | No | Thrown to the caller when triggered by an explicit call; on internal status polling, only |
| The upstream service returned an unrecognizable error response; the SDK has kept the original information in | No | Retryable; if it persists, investigate service availability together with |
| SDK remotely disabled (fully shut down or version too low); the reason is in | Yes (when the SDK detects a disable result, it automatically ends the in-progress experience; you can restart after recovery) | Prompt the user based on the reason in |
Note"Fatal?" refers specifically to whether it triggers the SDK to automatically terminate the current experience. Synchronous validation errors such as 100001/100002/103xxx only reject/throw for that call (100002 directly fails initialization) and do not terminate an experience.
Fatal vs non-fatal:
- Fatal errors: the SDK automatically terminates the current experience (disconnects the real-time connection, releases resources, calls
endTravel) and surfaces it viaonError; the host should clean up the current experience state and allow a restart. There are four categories: ① internal status polling reads an experience status offailed(e.g.,500001inference failure); ② real-time connection fatal (105001/105002/105003); ③ auto-end on no stream (105006: the first frame never arrived, or the stream was interrupted during running and did not recover before timeout, and the SDK proactively ends it); ④ SDK remotely disabled (108001). - Non-fatal errors: do not terminate the experience and are only surfaced via
onError; you mayupdateTokenagain or wait for the service / real-time channel to recover before continuing. A single failed request of internal status polling itself (network106001, parsing106002, upstream anomaly106003, business error5xxxxx) falls into this category—polling continues in the next cycle, and only when it reads a status offaileddoes it end the experience; authentication failure101001and an asynchronoussendCommandthrottled-flush send failure105004are likewise non-fatal. Note: the SDK never sends any keepalive payload of its own over the real-time channel, so105004cannot appear while the session is idle—it can only be triggered by your ownsendCommandcall.