Troubleshooting
Start with the visible symptom. Run the diagnostic in its row, then open the linked guide for the complete behavior.
Terminal and output
| Symptom | Diagnostic and first action | Canonical guide |
|---|---|---|
| Keys do not echo after exit | On POSIX, run stty -a. The flags -echo or -icanon show unrestored terminal input state. Run reset, then inspect every exit path. SIGKILL cannot run signal handlers or renderer.destroy(). | Lifecycle and cleanup |
| Logs overwrite or corrupt the UI | Read renderer.screenMode, renderer.externalOutputMode, and renderer.consoleMode. Also check OTUI_USE_ALTERNATE_SCREEN and OTUI_OVERRIDE_STDOUT. Use the console overlay for console.*, or use split-footer capture for stdout. Stderr remains separate. | Renderer output modes and Console overlay |
| Main-screen or split-footer rows remain damaged | Confirm the resolved screenMode. Call destroy() before the transport closes. A custom transport needs time to flush shutdown bytes. | Screen modes and custom cleanup |
Node.js and native loading
For a native crash, use Native crash debugging to load matching release symbols and collect a useful report.
| Symptom | Diagnostic and first action | Canonical guide |
|---|---|---|
OpenTUI native FFI is not available for this runtime yet | Run node --version. It must print v26.4.0 or later. Start the ESM app with node --experimental-ffi app.mjs. With --permission, also grant --allow-ffi and native-library read access. | Node.js runtime |
ERR_REQUIRE_ASYNC_MODULE | Find require("@opentui/core") or a CommonJS entry. Change the application to ESM and use import. | Node.js runtime |
| A native package or library is missing | Run bun -e 'console.log(process.platform, process.arch, process.env.OPENTUI_LIBC ? process.env.OPENTUI_LIBC : "glibc")'. Replace bun with node for a Node.js host. Check for the matching @opentui/core-* optional package. An install that omits optional dependencies can defer the failure until native work. | Native artifacts |
Linux reports a loader error for libopentui.so | Compare the selected libc with the target system. Set OPENTUI_LIBC=musl before Core imports on musl. On Alpine, install libstdc++ and libgcc. | Select the Linux libc |
OTUI_ASSET_ROOT must be an absolute directory | Print process.env.OTUI_ASSET_ROOT. Replace a relative value with an absolute path before Core imports. | OTUI_ASSET_ROOT |
Missing OpenTUI asset | Compare the missing key with the exact keys from build-time getNodeAssets(). A configured asset root has no package fallback. Extract or copy the complete asset set. | Runtime assets and Node.js SEA |
Runtime-loaded modules
| Symptom | Diagnostic and first action | Canonical guide |
|---|---|---|
| A runtime-plugin entry says it is Bun-only | Check process.versions.bun. Run the host with Bun. Node.js cannot use these entry points. | Runtime module loading |
| The installer says it is already installed without a specifier | Search the entry graph for a side-effect runtime-plugin support import. Remove it, then call the matching /configure installer once with the complete additional map. | Configure before loading |
| A plugin receives a different Core, React, or Solid singleton | Compare one exported object by identity in the host and plugin. Search for stacked Core and framework installers. Keep one host installer and one complete map. | Choose one installer |
| A second installer call does not replace an existing map value | Record the return values. The first compatible call returns true and later compatible calls return false. Existing keys keep their first installed entries. | Repeated installation |
| A Three.js import fails inside a plugin | Inspect the plugin's exact import. The first-party map exposes only @opentui/three. It does not expose three, three/webgpu, or three/tsl. Deploy those dependencies or add explicit trusted host mappings. | Default module maps |
A dependency under node_modules does not use the host runtime | Check its extension and nearest package.json. Rewriting there supports ESM only. A CommonJS helper that imports runtime modules is not rewritten. | Rewriting behavior |
Solid TSX under node_modules fails or uses the wrong JSX runtime | Check whether the package ships uncompiled JSX or TSX. The Solid transform excludes node_modules. Publish precompiled ESM or load source from outside that directory. | Solid transform order |
| A compiled executable cannot find a plugin | Log the resolved plugin URL. Check that the plugin file and every unmapped dependency exist outside the executable. Runtime support does not embed unknown sidecars. | Executable sidecars and deployment forms |
Integrations and protocols
| Symptom | Diagnostic and first action | Canonical guide |
|---|---|---|
Tree-sitter reports Worker initialization timed out or a generic highlight initialization error | Call await client.initialize() directly to retain the original error. Check the worker, WASM, parser, query, and data paths. Under Node.js permissions, allow workers, asset reads, and data-path writes. | Tree-sitter and runtime assets |
| An image uses Unicode blocks instead of Kitty or Sixel | Log renderer.capabilities, renderer.resolution, and image.effectiveProtocol. Auto mode uses blocks in tmux. Sixel also uses blocks until pixel resolution is available. Check protocol environment overrides. | Image rendering protocol |
| Image output breaks inside a multiplexer | Read renderer.capabilities?.multiplexer. Auto mode chooses blocks in tmux. Explicit Kitty or usable Sixel uses tmux passthrough. Remove unsupported forced protocols. | Image rendering protocol and terminal capabilities |
| An SSH clipboard operation affects the server | Read renderer.capabilities?.remote and the host and terminal result statuses. Host clipboard access targets the SSH server. Use terminal-only for the client clipboard. | Remote clipboard sessions |
triggerNotification() returns false | Read renderer.isDestroyed, renderer.capabilities?.notifications, and renderer.capabilities?.multiplexer. The method returns false after destruction or without a detected protocol. Check OPENTUI_NOTIFICATIONS and OPENTUI_NOTIFICATION_PROTOCOL. | Notifications |
| Notifications fail through tmux or Zellij | In tmux, enable the required passthrough policy. In Zellij, confirm OSC 99 forwarding support. Use an override only when detection misses supported forwarding. | Notification multiplexers |
ThreeRenderable init failed appears once and the view stays blank | Read the logged WebGPU error and check bun-webgpu 0.1.7. The renderable records an initialization failure and does not retry. Fix the cause, then create a new renderable. | ThreeRenderable lifecycle |
Test waits
| Symptom | Diagnostic and first action | Canonical guide |
|---|---|---|
Timed out waiting for visual idle after ... frames | Read the attached frameId, nativeFrameCount, cellsUpdated, and scheduler fields. Stop unintended live rendering or release the live request. Increase maxFrames only when the expected work needs more frames. | Waiting for observable output |
Native builds across worktrees
Zig shares its global cache by default. For repeated native builds across many worktrees, you can also share the project cache:
export ZIG_LOCAL_CACHE_DIR="$HOME/.cache/opentui/zig-local-0.16.0"
bun run build:nativeUse a persistent local directory. Use a separate directory for each Zig version. bun run clean does not remove an
external cache.
Next
Environment variables lists diagnostics and overrides.
Runtime and platform support owns version, target, permission, and asset rules.
Deploy an OpenTUI application covers release-specific failures.