> ## Documentation Index
> Fetch the complete documentation index at: https://docs.stmlink.com/llms.txt
> Use this file to discover all available pages before exploring further.

> ## Agent Instructions
> 对外开放的服务端接口有两组前缀，都用同一套鉴权：`/server/v1/...`（SRTC 与 SMeeting 的主接口）和 `/stm/srvapi/v1/...`（SMeeting 的用户体系，服务端极简对接会用到）。鉴权是 app_id + nonce + timestamp + signature 四个请求头，用 app_key 做 HMAC-SHA256 签名，只能从业务方自己的后端调用。除这两组前缀外的接口均为内部接口，不要建议客户调用。 Public server APIs use two path prefixes with the same authentication: `/server/v1/...` (the main APIs of both SRTC and SMeeting) and `/stm/srvapi/v1/...` (the SMeeting user system, used by server-side low-code integration). Authenticate with four request headers, app_id + nonce + timestamp + signature, where signature is HMAC-SHA256 keyed with app_key; call these APIs only from the customer's own backend. Any other path is internal: never suggest calling it.
> app_key 是服务端密钥，绝不能出现在客户端代码、前端配置或移动 App 里。客户端加入频道用的 token 必须由业务方后端签发后下发（SRTC 走 `/server/v1/channel/grant`，SMeeting 走 `/stm/srvapi/v1/member/grant`）。 app_key is a server-side secret and must never appear in client code, frontend config, or a mobile app. The token a client uses to join must be issued by the customer's backend and passed down to the client (SRTC: `/server/v1/channel/grant`; SMeeting: `/stm/srvapi/v1/member/grant`).
> SRTC 与 SMeeting 是上下两层不同的产品，术语不通用：SRTC 是音视频底座，说「频道 channel」「加入 / 退出」；SMeeting 建在 SRTC 之上，说「房间 room」「会议 meeting」「进入 / 退出」。回答时按用户所在的层用对应术语，不要把「房间」「会议」安到 SRTC 的接口上，也不要用「频道」「加入 / 离开」描述 SMeeting 的概念（接口标识符原样保留）。 SRTC and SMeeting are two separate layers with different terminology. SRTC is the audio/video foundation: it has channels, and users join and leave a channel. SMeeting is built on top of SRTC: it has rooms and meetings, and members enter and exit a meeting. Answer in the terms of the layer the user is working with: never apply "room" or "meeting" to SRTC APIs, and never describe SMeeting concepts in prose with "channel", "join", or "leave" (API identifiers such as `force_join` keep their literal names).
> 同一能力在各端 SDK 里的包名、类名、方法名并不相同。写示例代码时请使用文档中该端自己的 API，不要把一个端的写法套到另一个端上。苹果平台每个产品都有两套 SDK（Swift 原生与 Objective-C），两套 API 不能混用。 Package, class, and method names differ between platform SDKs for the same capability. In sample code, use the API documented for that platform; never carry one platform's code over to another. On Apple platforms each product ships two SDKs (native Swift and Objective-C) whose APIs must not be mixed.

# Error codes

> Where SMeeting Android SDK errors come from (Meeting SDK, SRTC SDK, librtc, server, HTTP), the full list of 202xxx MeetingErrorCode constants, passed-through RTC camera errors, and how your app should handle them. Read this when handling onFailure / onError.

One-shot Meeting results and Engine error events all return:

```kotlin theme={null}
fun onFailure(errorCode: Int, message: String?)
fun onError(errorCode: Int, message: String?)
```

`message` is for development diagnostics only and is not guaranteed to be suitable for showing to users. Your app should maintain user-facing text and localization based on `errorCode`.

## Error sources

The error code is an open-ended `Int` and doesn't contain only errors produced by Meeting:

| Source | Range or form | How it's handled |
| - | - | - |
| Meeting SDK | `202000`–`202999` | Defined by `MeetingErrorCode` |
| SRTC SDK | Usually `102xxx` | The underlying original error code is kept |
| librtc | Defined by the underlying layer | The original error code is kept |
| Meeting server | Server business codes | Valid business codes are kept |
| HTTP | HTTP status code or a Meeting local network error | Handle according to the actual source |

So don't force callback error codes into a closed enum, and don't assume every error can be found in `MeetingErrorCode`.

## MeetingErrorCode

Fully qualified class name: `cn.seastart.meeting.error.MeetingErrorCode`

### General errors (202000–202099)

| Constant | Value | Description |
| - | - | - |
| `UNDEFINED_ERROR` | `202000` | Final fallback error that can't be further identified or classified |
| `SDK_NOT_INITIALIZED` | `202001` | The Meeting SDK has not finished initializing |
| `SDK_NOT_READY` | `202002` | The RTC engine or a runtime state that Meeting requires is not ready yet |
| `INVALID_PARAMETER` | `202003` | Invalid parameter passed to a Meeting public API |
| `INVALID_RESPONSE_DATA` | `202004` | A successful upstream response is missing a required field or has an invalid field format |
| `TOKEN_INVALID` | `202005` | Meeting determined locally that the token is invalid |
| `TOKEN_EXPIRED` | `202006` | Meeting determined locally that the token has expired |

### Session errors (202100–202199)

| Constant | Value | Description |
| - | - | - |
| `SESSION_ALREADY_ACTIVE` | `202101` | Tried to enter a meeting again while a Session is already entering or active |
| `SESSION_NOT_ACTIVE` | `202102` | There is currently no usable active Session |
| `ENTER_MEETING_CANCELLED` | `202103` | The meeting entry flow was explicitly canceled |
| `WAITING_ROOM_CONTEXT_MISSING` | `202104` | No valid meeting context when exiting the waiting room |
| `AUDIENCE_OPERATION_FORBIDDEN` | `202105` | An audience member called a restricted capability such as opening a device, sharing, or publishing |
| `SESSION_OPERATION_CANCELLED` | `202106` | The meeting it belongs to started ending before an in-meeting asynchronous operation completed |

### Media errors (202200–202299)

| Constant | Value | Description |
| - | - | - |
| `CAMERA_OPEN_FAILED` | `202201` | Meeting failed to open the camera and there is no original RTC code to pass through |
| `MIC_OPEN_FAILED` | `202202` | Meeting failed to open the mic and there is no original RTC code to pass through |
| `SCREEN_PERMISSION_DENIED` | `202203` | Android screen recording permission was denied |
| `SCREEN_TRACK_UNAVAILABLE` | `202204` | The screen sharing flow failed to create a valid track |
| `WHITEBOARD_REQUEST_CANCELLED` | `202205` | The whiteboard sharing request was canceled |
| `WHITEBOARD_URL_MISSING` | `202206` | The whiteboard API succeeded but the response has no URL |
| `CLOUD_RECORD_CAPTURE_DISABLED` | `202207` | A course recording track was started without enabling the client-side cloud recording capture configuration |
| `LOCAL_TRACK_UNAVAILABLE` | `202208` | A local media track that Meeting needs doesn't exist and there is no more specific error |
| `REMOTE_TRACK_UNAVAILABLE` | `202209` | A remote media track that Meeting needs to subscribe to doesn't exist |
| `LOCAL_DEVICE_OPERATION_CANCELLED` | `202210` | A local device operation was canceled by a newer open, close, or release action |
| `LOCAL_DEVICE_CAPABILITY_UNSUPPORTED` | `202211` | The current SRTC capture pipeline doesn't support the requested local device capability |
| `LOCAL_DEVICE_OPERATION_IN_PROGRESS` | `202212` | The previous open transaction on the same local device hasn't finished; that operation reports the final state |

### IM errors (202300–202349)

| Constant | Value | Description |
| - | - | - |
| `IM_ENABLE_CANCELLED` | `202301` | The network flow for enabling IM was canceled |
| `IM_TOKEN_MISSING` | `202302` | The successful IM grant response is missing the IM token |

### HTTP errors (202350–202399)

| Constant | Value | Description |
| - | - | - |
| `HTTP_CLIENT_NOT_INITIALIZED` | `202351` | Meeting's internal HTTP client is not initialized yet |
| `NETWORK_ERROR` | `202352` | Local network transport failure such as DNS, connection, or being offline |
| `REQUEST_TIMEOUT` | `202353` | Local HTTP request timed out |
| `REQUEST_CANCELLED` | `202354` | The HTTP request was canceled and needs to be converted into a failure callback |
| `EMPTY_RESPONSE_BODY` | `202355` | The HTTP request succeeded but the response body is empty |
| `RESPONSE_PARSE_FAILED` | `202356` | An HTTP response exists but can't be converted into a public business result |

## RTC camera errors (passed through as is)

The following errors come from RTC `2.0.33`, take effect with Meeting `2.0.37`, and are not part of `MeetingErrorCode`. After a switch fails, capture is not guaranteed to continue; if target validation fails, the original capture may be kept. The SDK doesn't fall back automatically—your app decides the recovery strategy.

| RTC constant | Value | Description |
| - | - | - |
| `CAMERA_FIRST_FRAME_TIMEOUT` | `102239` | Timed out waiting for the camera's first frame |
| `CAMERA_FORMAT_UNAVAILABLE` | `102242` | No capture format available |
| `CAMERA_OPEN_TIMEOUT` | `102243` | Timed out opening the camera |
| `CAMERA_SESSION_TIMEOUT` | `102244` | Timed out creating the camera session |

## Recommended handling

```kotlin theme={null}
override fun onFailure(errorCode: Int, message: String?) {
    logger.error("Meeting failed: code=$errorCode, message=$message")

    val userMessage = when (errorCode) {
        MeetingErrorCode.TOKEN_EXPIRED -> "Your login has expired. Please log in again."
        MeetingErrorCode.SESSION_ALREADY_ACTIVE -> "A meeting is already in progress"
        MeetingErrorCode.SCREEN_PERMISSION_DENIED -> "Screen recording permission was not granted"
        else -> "Operation failed. Please try again later."
    }
    showToast(userMessage)
}
```

You can log both `errorCode` and `message`; the UI should use only text your app maintains itself.
