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 usage

Listening 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 2D

REQUIRED_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 filtering
  • WEBGL_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 with ohb-cli first

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-cli not 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 no gesture_wave clip
  • 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

CodeCategoryRecoverable
WEBGL2_NOT_SUPPORTEDenvironment
WEBGL_CONTEXT_LOSTenvironment
WEBGL_CONTEXT_CREATION_FAILEDenvironment
REQUIRED_EXTENSION_MISSINGenvironment
CANVAS_SIZE_INVALIDenvironment
ASSET_FETCH_FAILEDasset
ASSET_INVALID_MAGICasset
ASSET_VERSION_MISMATCHasset
ASSET_CRC_MISMATCHasset
ASSET_CHUNK_MISSINGasset
ASSET_PARSE_ERRORasset
TEXTURE_UPLOAD_FAILEDasset
ASSET_GPU_OOMasset
ANIMATION_CLIP_NOT_FOUNDruntime
ANIMATION_GRAPH_PARSE_ERRORruntime
ANIMATION_GRAPH_STATE_NOT_FOUNDruntime
MORPH_INDEX_OUT_OF_RANGEruntime
IK_CHAIN_NOT_FOUNDruntime
IK_DIVERGEDruntime
RENDER_FRAMEBUFFER_INCOMPLETEruntime
SHADER_COMPILE_ERRORruntime✓*
SHADER_LINK_ERRORruntime
STREAM_CONNECTION_FAILEDstreaming
STREAM_PROTOCOL_MISMATCHstreaming
STREAM_AUTH_FAILEDstreaming
STREAM_CHARACTER_NOT_FOUNDstreaming
STREAM_CAPACITY_EXCEEDEDstreaming
STREAM_DECODE_ERRORstreaming
STREAM_SEQUENCE_GAPstreaming
STREAM_LATENCY_HIGHstreaming
STREAM_DISCONNECTEDstreaming
WASM_INIT_FAILEDwasm
WASM_OOMwasm
WASM_INVALID_CHARACTERwasm
WASM_SIMD_UNSUPPORTEDwasm
LIPSYNC_AUDIO_CONTEXT_REQUIREDlipsync
LIPSYNC_VISEME_ID_INVALIDlipsync
LIPSYNC_QUEUE_OVERFLOWlipsync
LIPSYNC_IPA_UNKNOWNlipsync
API_NOT_INITIALIZEDapi
API_ALREADY_DESTROYEDapi
API_CANVAS_DETACHEDapi
API_INVALID_QUALITYapi
API_PARAM_NOT_FOUNDapi
API_CUSTOM_SHADER_INVALIDapi

* Custom shader errors fall back to the built-in shader (recoverable). Built-in shader link errors are not recoverable.


Next Steps