OpenHuman - Error Code Reference
Error Structure
All SDK errors are instances of OpenHumanError, extending the native Error class:
class OpenHumanError extends Error {
code: string // machine-readable error code (e.g. 'WEBGL2_NOT_SUPPORTED')
category: ErrorCategory
characterId?: number // which character triggered the error (if applicable)
detail?: unknown // additional structured context (varies per error)
recoverable: boolean // true = SDK may auto-retry; false = manual intervention needed
}
type ErrorCategory =
| "environment" // browser / GPU capability issues
| "asset" // .ohb loading and parsing errors
| "runtime" // animation, rendering errors during playback
| "streaming" // WebSocket / network errors
| "wasm" // WASM module errors
| "lipsync" // TTS / viseme scheduling errors
| "api" // incorrect SDK API usageListening for Errors
// Global error handler
human.on("error", (err: OpenHumanError) => {
console.error(`[${err.category}] ${err.code}: ${err.message}`)
if (!err.recoverable) {
// show fallback UI
}
})
// Async errors from loadCharacter, connectStream, etc.
try {
await human.loadCharacter("./character.ohb")
} catch (err) {
if (err instanceof OpenHumanError) {
handleError(err)
}
}Category: environment
Browser or GPU capability errors. These typically surface at new OpenHuman() or loadCharacter().
WEBGL2_NOT_SUPPORTED
Message: WebGL 2.0 is not supported in this browser or context.
Recoverable: No
Causes:
- Browser too old (IE, old Safari < 15)
- WebGL disabled in browser flags
- Running in a sandboxed iframe without
allow-scripts - Hardware GPU blacklisted by browser
Fix:
// Check before initializing
if (!document.createElement("canvas").getContext("webgl2")) {
showFallbackUI("Your browser does not support WebGL 2.0.")
return
}Minimum browser versions with WebGL 2.0: Chrome 56, Firefox 51, Safari 15, Edge 79.
WEBGL_CONTEXT_LOST
Message: WebGL context was lost. Attempting recovery.
Recoverable: Yes (SDK auto-recovers)
Causes:
- GPU driver crash or reset
- Too many WebGL contexts on the page (browsers limit to 8–16)
- System sleep/wake cycle on some mobile GPUs
- Page was backgrounded for too long
Behavior: The SDK listens for webglcontextlost and webglcontextrestored events. On restore, all GPU resources (textures, buffers, framebuffers) are automatically re-uploaded. Expect a 1–3 second freeze during recovery.
Fix: Reduce the number of simultaneous OpenHuman instances. Call human.destroy() on instances no longer visible.
WEBGL_CONTEXT_CREATION_FAILED
Message: Failed to create WebGL 2.0 context on the provided canvas.
Recoverable: No
Causes:
- Canvas already has a 2D context (
canvas.getContext('2d')was called first) powerPreference: 'low-power'not supported by GPU- Canvas is 0×0 pixels
Fix:
// Never mix 2D and WebGL contexts on the same canvas
// Use separate canvas elements for each
const glCanvas = document.getElementById("character-canvas")
const uiCanvas = document.getElementById("ui-canvas") // separate canvas for 2DREQUIRED_EXTENSION_MISSING
Message: Required WebGL extension '{name}' is not available.
Recoverable: No
Detail: { extension: string }
Required extensions:
EXT_color_buffer_float- HDR framebuffers (RGBA16F)OES_texture_float_linear- HDR texture filteringWEBGL_depth_texture- shadow map
Fix: Check human.getCapabilities() before loading. If a required extension is missing, fall back to quality: 'low' which avoids HDR framebuffers.
const caps = human.getCapabilities()
if (!caps.hdrFramebuffers) {
human.setQuality("low")
}CANVAS_SIZE_INVALID
Message: Canvas dimensions are invalid: {width}×{height}.
Recoverable: No
Causes:
- Canvas is 0×0 (hidden element, display:none)
- Canvas is larger than
gl.MAX_VIEWPORT_DIMS(typically 16384×16384)
Fix: Ensure the canvas has non-zero dimensions before initializing. Use ResizeObserver to handle dynamic resizing.
Category: asset
Errors during .ohb bundle loading, parsing, and GPU upload.
ASSET_FETCH_FAILED
Message: Failed to fetch character bundle from '{url}': {httpStatus}
Recoverable: Yes (retry with human.loadCharacter())
Detail: { url: string, status: number, statusText: string }
Causes:
- Wrong URL / 404
- CORS headers missing on the server
- Network timeout
Fix:
// Add retry logic
async function loadWithRetry(url: string, retries = 3) {
for (let i = 0; i < retries; i++) {
try {
await human.loadCharacter(url)
return
} catch (err) {
if (err.code !== "ASSET_FETCH_FAILED" || i === retries - 1) throw err
await sleep(1000 * (i + 1))
}
}
}ASSET_INVALID_MAGIC
Message: File does not appear to be a valid .ohb bundle (invalid magic bytes).
Recoverable: No
Causes:
- Wrong file served at the URL (HTML 404 page, etc.)
- File corrupted in transit
- File is a raw
.glb- must be compiled withohb-clifirst
ASSET_VERSION_MISMATCH
Message: Bundle version {bundleMajor}.{bundleMinor} is not compatible with SDK version {sdkVersion}.
Recoverable: No
Detail: { bundleMajor: number, bundleMinor: number, sdkVersion: string }
Causes:
- Bundle compiled with a future version of
ohb-clinot yet supported by this SDK - Very old bundle (major version mismatch)
Fix: Recompile the bundle with the ohb-cli version matching the SDK version.
ASSET_CRC_MISMATCH
Message: Bundle chunk '{chunkType}' failed CRC32 validation. File may be corrupted.
Recoverable: Yes (retry fetch)
Detail: { chunkType: string, expected: number, actual: number }
Causes:
- Network corruption during download
- Partial download (server closed connection early)
- CDN serving stale partial file
ASSET_CHUNK_MISSING
Message: Required chunk '{chunkType}' not found in bundle.
Recoverable: No
Detail: { chunkType: string }
Required chunks: META, MESH, TEX, SKEL, MRPH, ANIM. All must be present.
ASSET_PARSE_ERROR
Message: Failed to parse bundle chunk '{chunkType}': {reason}
Recoverable: No
Detail: { chunkType: string, reason: string, byteOffset?: number }
Causes:
- Corrupt chunk data
- Bundle compiled with incompatible options
TEXTURE_UPLOAD_FAILED
Message: GPU texture upload failed for '{textureName}': {reason}
Recoverable: No
Detail: { textureName: string, reason: string }
Causes:
- Texture dimensions exceed
gl.MAX_TEXTURE_SIZE(typically 16384) - Compressed texture format not supported by GPU (BC7 on mobile)
- Out of GPU memory during upload
Fix: The SDK automatically falls back from BC7 to ETC2 to uncompressed RGBA8. If this error still fires, the GPU is out of memory - use quality: 'low' to reduce texture sizes.
ASSET_GPU_OOM
Message: GPU out of memory during character asset upload.
Recoverable: Partial (destroy other instances first)
Causes:
- Too many characters loaded simultaneously
- System GPU memory pressure (other browser tabs)
Fix:
human.on("error", async (err) => {
if (err.code === "ASSET_GPU_OOM") {
// Destroy lowest-priority character and retry
await lowPriorityHuman.destroy()
await human.loadCharacter("./character.ohb")
}
})Category: runtime
Errors during active rendering and animation playback.
ANIMATION_CLIP_NOT_FOUND
Message: Animation clip '{clipName}' not found in bundle.
Recoverable: Yes (falls back to idle)
Detail: { clipName: string, availableClips: string[] }
Causes:
- Calling
human.play('gesture_wave')when the bundle has nogesture_waveclip - Typo in clip name
ANIMATION_GRAPH_PARSE_ERROR
Message: Failed to parse animation graph: {reason}
Recoverable: No
Detail: { reason: string, line?: number }
Causes:
- Invalid JSON in graph definition
- Unknown state referenced in transition
- Parameter referenced in condition not declared in
parameters
ANIMATION_GRAPH_STATE_NOT_FOUND
Message: Animation graph state '{stateName}' not found.
Recoverable: Yes (stays in current state)
Detail: { stateName: string }
MORPH_INDEX_OUT_OF_RANGE
Message: Morph target index {index} is out of range (max: 51).
Recoverable: Yes (ignored)
Detail: { index: number }
IK_CHAIN_NOT_FOUND
Message: IK chain '{chainId}' not found in skeleton.
Recoverable: Yes (IK target ignored)
IK_DIVERGED
Message: IK solver for chain '{chainId}' failed to converge within {maxIterations} iterations.
Recoverable: Yes (uses last valid IK pose)
Detail: { chainId: string, maxIterations: number, finalError: number }
Fix: Increase maxIterations or check that the IK target is reachable within the chain's joint limits.
RENDER_FRAMEBUFFER_INCOMPLETE
Message: Framebuffer '{name}' is incomplete: {reason}
Recoverable: No
Detail: { name: string, reason: string, glStatus: number }
Causes:
- Canvas resized to 0×0 during active rendering
- GPU driver bug (rare)
SHADER_COMPILE_ERROR
Message: Shader compile error in '{shaderName}': {glslError}
Recoverable: No (falls back to built-in if custom shader)
Detail: { shaderName: string, glslError: string, source: string }
Causes:
- Syntax error in custom shader source
- Using GLSL features not supported in GLSL ES 3.00
Fix: Check the detail.glslError field for line numbers and description.
SHADER_LINK_ERROR
Message: Shader program link error: {reason}
Recoverable: No
Detail: { reason: string }
Category: streaming
WebSocket connection and streaming protocol errors.
STREAM_CONNECTION_FAILED
Message: Failed to connect to streaming server at '{url}'.
Recoverable: Yes (auto-retry with backoff)
Detail: { url: string, attempt: number, nextRetryMs: number }
STREAM_PROTOCOL_MISMATCH
Message: Server did not accept OHP protocol. Expected 'ohp.1.0'.
Recoverable: No
Causes:
- Server is not an OHP-compatible server
- Server running a different protocol version
STREAM_AUTH_FAILED
Message: Streaming server rejected authentication token.
Recoverable: No (token refresh required)
Fix:
human.on("error", async (err) => {
if (err.code === "STREAM_AUTH_FAILED") {
const newToken = await refreshAuthToken()
await human.connectStream(url, { auth: { token: newToken } })
}
})STREAM_CHARACTER_NOT_FOUND
Message: Server does not recognize character '{characterName}'.
Recoverable: No
Detail: { characterName: string }
STREAM_CAPACITY_EXCEEDED
Message: Streaming server is at maximum capacity.
Recoverable: Yes (retry after delay)
STREAM_DECODE_ERROR
Message: Failed to decode streaming frame: {reason}
Recoverable: Yes (frame dropped, stream continues)
Detail: { reason: string, opcode: number, frameSize: number }
Causes:
- Malformed binary frame from server
- Encoding mismatch (server sends f32, client expects i16)
STREAM_SEQUENCE_GAP
Message: Sequence gap detected: expected {expected}, received {actual}. {dropped} frames dropped.
Recoverable: Yes (jitter buffer compensates)
Detail: { expected: number, actual: number, dropped: number }
This is an informational warning (not a fatal error). Emitted when network packet loss causes missing frames. The jitter buffer holds the last known pose until the next valid frame arrives.
STREAM_LATENCY_HIGH
Message: Stream latency exceeds threshold: {latencyMs}ms (threshold: {thresholdMs}ms).
Recoverable: Yes
Detail: { latencyMs: number, thresholdMs: number }
Emitted when measured jitter buffer latency exceeds maxDelay. The SDK automatically re-syncs by skipping ahead in the buffer.
Fix: Use a streaming server geographically closer to users, or increase maxDelay.
STREAM_DISCONNECTED
Message: Stream disconnected: {reason}
Recoverable: Yes (auto-retry)
Detail: { reason: string, code: number }
Normal WebSocket close codes: 1000 (normal), 1001 (going away), 1006 (abnormal).
Category: wasm
WASM module errors.
WASM_INIT_FAILED
Message: WebAssembly module failed to instantiate: {reason}
Recoverable: No
Causes:
- Browser does not support WebAssembly (extremely rare, < 0.5% of users)
- WASM binary failed integrity check
- Memory allocation failed on instantiation
WASM_OOM
Message: WebAssembly heap memory exhausted (limit: {limitMB}MB).
Recoverable: Partial (destroy unused characters)
Detail: { limitMB: number, usedMB: number }
Default WASM heap limit: 256 MB. Each character uses ~20–30 MB of WASM heap.
WASM_INVALID_CHARACTER
Message: WASM character ID {id} is not registered.
Recoverable: No
Causes: Internal SDK error - character was destroyed but references remain. File a bug report.
WASM_SIMD_UNSUPPORTED
Message: WASM SIMD build loaded on unsupported browser. Falling back to scalar build.
Recoverable: Yes (automatic fallback, ~3× slower)
This is an informational warning. The SDK automatically falls back to the scalar WASM build. Performance degrades but rendering continues correctly.
Category: lipsync
TTS and viseme scheduling errors.
LIPSYNC_AUDIO_CONTEXT_REQUIRED
Message: scheduleViseme() requires an AudioContext to synchronize timing.
Recoverable: Yes
Fix: Pass audioContext parameter to scheduleViseme().
LIPSYNC_VISEME_ID_INVALID
Message: Viseme ID {id} is out of range (valid: 0–21 for Azure, named strings for OpenHuman).
Recoverable: Yes (viseme skipped)
LIPSYNC_QUEUE_OVERFLOW
Message: Viseme queue overflow: {count} pending visemes exceeds limit of {limit}.
Recoverable: Yes (oldest visemes dropped)
Causes: Speaking very long text without pauses, or scheduling visemes much faster than they are consumed.
Fix: Call human.clearVisemeQueue() before scheduling a new speech segment.
LIPSYNC_IPA_UNKNOWN
Message: Unknown IPA phoneme '{ipa}'. Mapping to silence.
Recoverable: Yes
Detail: { ipa: string }
Category: api
Incorrect SDK usage.
API_NOT_INITIALIZED
Message: OpenHuman instance is not fully initialized. Call loadCharacter() first.
Recoverable: Yes
Causes: Calling human.play(), human.setParam(), etc. before loadCharacter() resolves.
API_ALREADY_DESTROYED
Message: This OpenHuman instance has been destroyed and cannot be reused.
Recoverable: No
Fix: Create a new OpenHuman instance.
API_CANVAS_DETACHED
Message: The canvas element has been removed from the DOM.
Recoverable: No
Fix: Ensure the canvas stays in the DOM for the lifetime of the OpenHuman instance. Call human.destroy() before removing the canvas.
API_INVALID_QUALITY
Message: Invalid quality preset '{value}'. Must be 'high', 'medium', or 'low'.
Recoverable: Yes (defaults to 'medium')
API_PARAM_NOT_FOUND
Message: Parameter '{name}' not declared in animation graph.
Recoverable: Yes (call ignored)
Fix: Declare the parameter in the graph JSON under "parameters" before calling setParam().
API_CUSTOM_SHADER_INVALID
Message: Custom shader '{name}' is not a recognized shader slot.
Recoverable: Yes (ignored)
Detail: { name: string, validSlots: string[] }
Error Code Quick Reference
| Code | Category | Recoverable |
|---|---|---|
WEBGL2_NOT_SUPPORTED | environment | ✗ |
WEBGL_CONTEXT_LOST | environment | ✓ |
WEBGL_CONTEXT_CREATION_FAILED | environment | ✗ |
REQUIRED_EXTENSION_MISSING | environment | ✗ |
CANVAS_SIZE_INVALID | environment | ✗ |
ASSET_FETCH_FAILED | asset | ✓ |
ASSET_INVALID_MAGIC | asset | ✗ |
ASSET_VERSION_MISMATCH | asset | ✗ |
ASSET_CRC_MISMATCH | asset | ✓ |
ASSET_CHUNK_MISSING | asset | ✗ |
ASSET_PARSE_ERROR | asset | ✗ |
TEXTURE_UPLOAD_FAILED | asset | ✗ |
ASSET_GPU_OOM | asset | ✓ |
ANIMATION_CLIP_NOT_FOUND | runtime | ✓ |
ANIMATION_GRAPH_PARSE_ERROR | runtime | ✗ |
ANIMATION_GRAPH_STATE_NOT_FOUND | runtime | ✓ |
MORPH_INDEX_OUT_OF_RANGE | runtime | ✓ |
IK_CHAIN_NOT_FOUND | runtime | ✓ |
IK_DIVERGED | runtime | ✓ |
RENDER_FRAMEBUFFER_INCOMPLETE | runtime | ✗ |
SHADER_COMPILE_ERROR | runtime | ✓* |
SHADER_LINK_ERROR | runtime | ✗ |
STREAM_CONNECTION_FAILED | streaming | ✓ |
STREAM_PROTOCOL_MISMATCH | streaming | ✗ |
STREAM_AUTH_FAILED | streaming | ✗ |
STREAM_CHARACTER_NOT_FOUND | streaming | ✗ |
STREAM_CAPACITY_EXCEEDED | streaming | ✓ |
STREAM_DECODE_ERROR | streaming | ✓ |
STREAM_SEQUENCE_GAP | streaming | ✓ |
STREAM_LATENCY_HIGH | streaming | ✓ |
STREAM_DISCONNECTED | streaming | ✓ |
WASM_INIT_FAILED | wasm | ✗ |
WASM_OOM | wasm | ✓ |
WASM_INVALID_CHARACTER | wasm | ✗ |
WASM_SIMD_UNSUPPORTED | wasm | ✓ |
LIPSYNC_AUDIO_CONTEXT_REQUIRED | lipsync | ✓ |
LIPSYNC_VISEME_ID_INVALID | lipsync | ✓ |
LIPSYNC_QUEUE_OVERFLOW | lipsync | ✓ |
LIPSYNC_IPA_UNKNOWN | lipsync | ✓ |
API_NOT_INITIALIZED | api | ✓ |
API_ALREADY_DESTROYED | api | ✗ |
API_CANVAS_DETACHED | api | ✗ |
API_INVALID_QUALITY | api | ✓ |
API_PARAM_NOT_FOUND | api | ✓ |
API_CUSTOM_SHADER_INVALID | api | ✓ |
* Custom shader errors fall back to the built-in shader (recoverable). Built-in shader link errors are not recoverable.
Next Steps
- Architecture Overview
- GPU Optimization Guide -
ASSET_GPU_OOMmitigation - Streaming Protocol Spec - stream error codes
- WASM Runtime Docs - WASM error internals