This guide covers common issues when working with Kraken TUI and how to resolve them.
Build Problems
Native Library Not Found
Symptom:
Error: Unable to load native library
DLOpen error: libkraken_tui.so: cannot open shared object file
Solution:
# Build the native library first
cargo build --manifest-path native/Cargo.toml --release
# Verify the artifact exists
ls native/target/release/libkraken_tui. *
The native library must be built in release mode for optimal performance.
Symptom:
error: package `kraken_tui v0.1.0` cannot be built because it requires rustc 1.93 or newer
Solution:
# Update Rust toolchain
rustup update stable
rustc --version # Should be 1.93.x or newer
Missing Dependencies
Symptom:
error: linking with `cc` failed
/usr/bin/ld: cannot find -lxcb
Solution (Linux):
# Debian/Ubuntu
sudo apt-get install libxcb-dev
# Fedora/RHEL
sudo dnf install libxcb-devel
Solution (macOS):
# Usually no additional dependencies needed
# If issues occur, install Xcode Command Line Tools
xcode-select --install
Windows Build Issues
Symptom:
error: linker `link.exe` not found
Solution:
# Install Visual Studio Build Tools with C++ support
# Or use rustup's MSVC toolchain
rustup default stable-x86_64-pc-windows-msvc
Runtime Errors
Context Not Initialized
Symptom:
KrakenError : context not initialized
Cause: Attempting operations before calling Kraken.init() or after shutdown().
Solution:
const app = Kraken . init (); // Must be first
const box = new Box ();
app . setRoot ( box );
app . render ();
app . shutdown (); // Must be last
// Don't use app after shutdown()
Invalid Handle Errors
Symptom:
KrakenError : invalid handle : 42
Cause: Using a widget after it’s been destroyed.
Solution:
const box = new Box ();
box . destroy ();
// DON'T do this:
// box.setStyle({ fg: "red" }); // Error!
// Instead, create a new widget:
const newBox = new Box ();
newBox . setStyle ({ fg: "red" });
Tree Structure Violations
Symptom:
KrakenError : node 5 already has parent 3
Cause: Attempting to append a widget that’s already in the tree.
Solution:
const parent1 = new Box ();
const parent2 = new Box ();
const child = new Text ({ content: "Hello" });
parent1 . append ( child );
// To move to parent2:
parent1 . removeChild ( child );
parent2 . append ( child );
Encoding Errors
Symptom:
KrakenError : invalid UTF - 8
Cause: Passing non-UTF-8 bytes to FFI functions.
Solution:
// CORRECT: Use TextEncoder
const text = "Hello, 世界" ;
const utf8 = new TextEncoder (). encode ( text );
ffi . tui_set_content ( handle , ptr ( utf8 ), utf8 . byteLength );
// WRONG: Don't pass arbitrary bytes
const invalid = new Uint8Array ([ 0xFF , 0xFE ]);
// ffi.tui_set_content(handle, ptr(invalid), 2); // Error!
High Frame Time
Symptom: Sluggish rendering, >16ms per frame
Diagnosis:
app . render ();
const layoutUs = ffi . tui_get_perf_counter ( 0 );
const renderUs = ffi . tui_get_perf_counter ( 1 );
const diffCells = ffi . tui_get_perf_counter ( 2 );
console . log ( `Layout: ${ layoutUs } µs, Render: ${ renderUs } µs, Diff: ${ diffCells } cells` );
if ( layoutUs > 8000 ) {
console . warn ( "Layout is slow - check widget count and nesting depth" );
}
Solutions:
// SLOW: Sets dirty flag 3 times
widget . setStyle ({ fg: "red" });
widget . setStyle ({ bg: "blue" });
widget . setStyle ({ bold: true });
// FAST: Single dirty flag
widget . setStyle ({
fg: "red" ,
bg: "blue" ,
bold: true
});
Use ScrollBox for large content
// For large text content, use ScrollBox to clip rendering
const scrollBox = new ScrollBox ({ width: 80 , height: 24 });
const largeText = new Text ({ content: veryLargeString });
scrollBox . append ( largeText );
Event Buffer Buildup
Symptom: Delayed or dropped input events
Diagnosis:
const eventDepth = ffi . tui_get_perf_counter ( 3 );
if ( eventDepth > 50 ) {
console . warn ( `Event buffer has ${ eventDepth } pending events` );
}
Solution:
// Drain events more frequently
while ( running ) {
app . readInput ( 16 ); // Read input every frame
// Process ALL events
for ( const event of app . drainEvents ()) {
handleEvent ( event );
}
app . render ();
}
Memory Leaks
Symptom: Growing memory usage, increasing node count
Diagnosis:
const nodeCount = ffi . tui_get_node_count ();
console . log ( `Active widgets: ${ nodeCount } ` );
Solutions:
Use destroySubtree() for cleanup
// WRONG: Orphans children
parent . destroy ();
// CORRECT: Recursively destroys entire subtree
parent . destroySubtree ();
Cancel animations on unmount
const animHandle = widget . animate ({
property: "opacity" ,
target: 0 ,
duration: 500
});
// Later, before destroying widget:
widget . cancelAnimation ( animHandle );
widget . destroy ();
Clear themes when no longer needed
const theme = new Theme ();
theme . setColor ( ThemeKey . FG , 0xFF00FF00 );
app . applyTheme ( rootWidget , theme );
// Later:
app . clearTheme ( rootWidget );
theme . destroy ();
Terminal Issues
Colors Not Displaying
Symptom: Colors appear as gray or monochrome
Diagnosis:
const caps = ffi . tui_get_capabilities ();
if (( caps & 0x01 ) === 0 ) {
console . log ( "Terminal lacks truecolor support" );
}
Solutions:
Set COLORTERM=truecolor environment variable
Use a modern terminal emulator (iTerm2, Alacritty, Windows Terminal)
Colors automatically degrade: truecolor → 256-color → 16-color → monochrome
Mouse Events Not Working
Symptom: Click events not firing
Diagnosis:
const caps = ffi . tui_get_capabilities ();
if (( caps & 0x02 ) === 0 ) {
console . log ( "Terminal lacks mouse support" );
}
Solutions:
Enable mouse reporting in terminal settings
Test with known-good terminal (Alacritty, Windows Terminal)
Keyboard focus traversal remains operational
Terminal Size Detection
Symptom: Layout doesn’t fit terminal
Solution:
const width = new Uint16Array ( 1 );
const height = new Uint16Array ( 1 );
ffi . tui_get_terminal_size ( ptr ( width ), ptr ( height ));
console . log ( `Terminal: ${ width [ 0 ] } cols × ${ height [ 0 ] } rows` );
Terminal Not Restored on Exit
Symptom: Terminal remains in raw mode after crash
Solution:
process . on ( "SIGINT" , () => {
try {
app . shutdown (); // Restore terminal
} catch ( error ) {
console . error ( "Shutdown failed:" , error );
}
process . exit ( 0 );
});
process . on ( "uncaughtException" , ( error ) => {
console . error ( "Fatal error:" , error );
try {
app . shutdown ();
} catch {}
process . exit ( 1 );
});
Debugging Workflow
Step 1: Enable Debug Mode
const app = Kraken . init ();
ffi . tui_set_debug ( 1 );
// Now stderr will show:
// - Tree mutations (append, remove, destroy)
// - Layout recomputations
// - Dirty flag propagation
// - Event buffer state
// - Hit-test traces
function logDiagnostics () : void {
console . error ( "=== Diagnostics ===" );
console . error ( `Layout: ${ ffi . tui_get_perf_counter ( 0 ) } µs` );
console . error ( `Render: ${ ffi . tui_get_perf_counter ( 1 ) } µs` );
console . error ( `Diff cells: ${ ffi . tui_get_perf_counter ( 2 ) } ` );
console . error ( `Event buffer: ${ ffi . tui_get_perf_counter ( 3 ) } ` );
console . error ( `Total nodes: ${ ffi . tui_get_perf_counter ( 4 ) } ` );
console . error ( `Dirty nodes: ${ ffi . tui_get_perf_counter ( 5 ) } ` );
console . error ( `Active animations: ${ ffi . tui_get_perf_counter ( 6 ) } ` );
}
Step 3: Validate State
function validateWidget ( widget : Widget ) : boolean {
if ( widget . handle === 0 ) {
console . error ( "Invalid handle: widget was destroyed" );
return false ;
}
const nodeCount = ffi . tui_get_node_count ();
if ( nodeCount === 0 ) {
console . error ( "No nodes exist - context may be uninitialized" );
return false ;
}
return true ;
}
Step 4: Isolate the Issue
// Minimal reproduction
const app = Kraken . init ();
ffi . tui_set_debug ( 1 );
const box = new Box ({ width: "100%" , height: "100%" });
app . setRoot ( box );
console . log ( "Before render" );
app . render ();
console . log ( "After render" );
app . shutdown ();
Getting Help
If you encounter an issue not covered here:
Check error message : Kraken errors include detailed context
Enable debug mode : ffi.tui_set_debug(1)
Review diagnostics : Use performance counters
Create minimal reproduction : Isolate the issue
Report the issue : Include version, platform, and code sample