You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
First ctx.completion() throws Exception in HostFunction: <unknown> on Samsung Exynos (qwen3-0.6b / TinyLlama / gemma4-e2b-lite, both with and without no_extra_bufts: true) #336
llama.rn versions tested:0.12.0-rc.4 (cpp pin: build b8665, commit b863507) and 0.12.0-rc.9 (cpp pin: build b8827, commit 5e6c0e1). All RC versions in the rc.5 → rc.9 range reproduce the same JS-layer error.
React Native: 0.81.5, Hermes engine
Expo SDK: 54
Devices reproducing the crash (both Samsung Exynos):
APK type: debug build, sideloaded via adb install -r. JS bundle served by npx expo start Metro at runtime (no in-APK bundle).
Snapdragon / MediaTek devices: not yet tested. We are explicitly trying to make the Exynos path work rather than gate it off, so cross-platform falsification is pending hardware.
Failure mode
The very first call to ctx.completion(...) after a successful cold init fails on the JS side with:
[ReasoningService] completeWithReasoning failed: [Error: Exception in HostFunction: <unknown>]
[ReasoningService] JSI HostFunction crash detected — disabling reasoning for the rest of this session.
After this throw, the process stays alive but every subsequent ctx.completion(...) returns immediately because the consuming app trips a JSI-corruption circuit-breaker (our F5 — see "What's been ruled out" below). Any ctx.completion(...) calls already in flight at the moment of the throw eventually time out at our 30 s wrapper timeout.
The <unknown> is React Native + Hermes' generic fallback wrapper for any C++ throw whose .what() it can't translate (RN #34730). PR #318's resultGenerator try/catch does not catch this throw — <unknown> persists in rc.9. Either the throw is not in the resultGenerator path, is not std::exception-derived, or fires before the resultGenerator is reached.
Reproducible steps
npm install llama.rn@^0.12.0-rc.9. Native artifacts download via the rc.5+ postinstall script.
Call initLlamaContext with these params (matches what we use in production):
Load any of: qwen3-0.6b-Q4_K_M, tinyllama-1.1b-chat-v1.0.Q4_K_M, gemma-4-E2B-it-UD-IQ3_XXS. We've seen the same failure on all three.
Cold init succeeds. loadModel logs Context initialized with n_seq_max = 1 and attachThreadpoolsIfAvailable: Attached ggml threadpool (n_threads=4, n_threads_batch=4). Latency: ~3.3 s on A50 with TinyLlama 1.1B Q4_K_M, ~10.8 s on A25 with gemma4-e2b-lite IQ3_XXS.
Call ctx.completion({ messages: [{role:'system',content:...},{role:'user',content:...}], n_predict: 512, temperature: 0.1, stop: [...] }) with a ~150–250-token prompt.
loadPrompt logs Input processed: n_past=N, embd.size=M for the prompt batch.
~30 ms after the prompt-processed log, the Promise rejects with [Error: Exception in HostFunction: <unknown>].
Expected: completion returns a token stream, OR if it errors, the JS-side error has a meaningful .what() string.
Observed: opaque <unknown> on every variant tested.
Native tombstone
The opaque <unknown> is the catchable-exception code path. There's a second path that surfaces the underlying abort directly: llama.rn 0.12.0-rc.4 + no_extra_bufts: true produces a SIGABRT in lm_ggml_abort from llm_graph_context::build_inp_embd, with the process dying. This is a separate bug specific to rc.4's older cpp pin (b8665 / b863507); it is fixed in rc.9's pin (b8827 / 5e6c0e1). I include it because (a) the abort site gives us the only native stack trace we have for this device/model combination, and (b) the call stack lands inside the same decode → process_ubatch → build_graph → build_inp_embd path that the <unknown> variant exits through.
05-02 10:07:54.019 F libc: Fatal signal 6 (SIGABRT), code -1 (SI_QUEUE) in tid 13270 (mqt_v_native), pid 30806
05-02 10:07:54.578 F DEBUG: pid: 30806, tid: 13270, name: mqt_v_native >>> com.eckonode.callibear <<<
05-02 10:07:54.578 F DEBUG: signal 6 (SIGABRT), code -1 (SI_QUEUE), fault addr --------
05-02 10:07:54.578 F DEBUG: ABI: 'arm64'
05-02 10:07:54.578 F DEBUG: Build fingerprint: 'samsung/a50sq/a50:11/RP1A.200720.012/A505USQSKDWB1:user/release-keys'
05-02 10:07:54.589 F DEBUG: backtrace:
05-02 10:07:54.589 F DEBUG: #00 pc 0000000000089cd0 /apex/com.android.runtime/lib64/bionic/libc.so (abort+164)
05-02 10:07:54.589 F DEBUG: #01 pc 0000000000303e0c base.apk!librnllama_v8.so (lm_ggml_abort+224) BuildId: 42fed8ee605fc9fabb2de8a1bb5d8e18c892eb6d
05-02 10:07:54.590 F DEBUG: #02 pc 000000000044957c base.apk!librnllama_v8.so (llm_graph_context::build_inp_embd(lm_ggml_tensor*) const+1612)
05-02 10:07:54.590 F DEBUG: #03 pc 00000000007648a4 base.apk!librnllama_v8.so (llm_build_llama<false>::llm_build_llama(llama_model const&, llm_graph_params const&)+160)
05-02 10:07:54.590 F DEBUG: #04 pc 00000000004fc798 base.apk!librnllama_v8.so
05-02 10:07:54.590 F DEBUG: #05 pc 00000000004fc1bc base.apk!librnllama_v8.so (llama_model::build_graph(llm_graph_params const&) const+1296)
05-02 10:07:54.590 F DEBUG: #06 pc 000000000041d934 base.apk!librnllama_v8.so (llama_context::process_ubatch(llama_ubatch const&, llm_graph_type, llama_memory_context_i*, lm_ggml_status&)+908)
05-02 10:07:54.591 F DEBUG: #07 pc 000000000041f2f0 base.apk!librnllama_v8.so (llama_context::decode(llama_batch const&)+2092)
05-02 10:07:54.591 F DEBUG: #08 pc 0000000000717664 base.apk!librnllama_v8.so (rnllama::llama_rn_context_completion::nextToken()+200)
05-02 10:07:54.591 F DEBUG: #09 pc 000000000071844c base.apk!librnllama_v8.so (rnllama::llama_rn_context_completion::doCompletion()+56)
05-02 10:07:54.591 F DEBUG: #10 pc 000000000006cda8 base.apk!librnllama_jni_v8.so BuildId: d6357eb959de320ac4efc2c34747afc1eec2ae72
05-02 10:07:54.591 F DEBUG: #11 pc 0000000000091cf0 base.apk!librnllama_jni_v8.so
05-02 10:07:54.592 F DEBUG: #12 pc 000000000009af94 base.apk!librnllama_jni_v8.so
05-02 10:07:54.592 F DEBUG: #13 pc 00000000000eb7a8 libc.so (__pthread_start(void*)+64)
05-02 10:07:54.592 F DEBUG: #14 pc 000000000008bc8c libc.so (__start_thread+64)
05-02 10:07:58.963 I Zygote: Process 30806 exited due to signal 6 (Aborted)
The crashing thread is named mqt_v_native (React Native's mqt JSI worker thread). On the A50 (Cortex-A53, ARMv8.0-A baseline) the picker correctly selects librnllama_v8.so (compiled with -march=armv8-a, no extensions) — confirmed via RNLlama.java:190 reading /proc/cpuinfo features fp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid (no asimddp, no i8mm, no fphp, no atomics).
Variant matrix (every combination tested on the A50)
llama.rn
no_extra_bufts
Outcome
JS error string
1
rc.4
not set
F5 trip, app survives
<unknown>
2
rc.4
true
SIGABRT in build_inp_embd, process dies
<unknown> (then abort)
3
rc.9
not set
F5 trip, app survives
<unknown>
4
rc.9
true
F5 trip, app survives
<unknown>
A25 5G reproduces variants 1, 3, 4 with the same JS-layer signature (different active model: gemma4-e2b-lite — confirms the bug is not model-specific).
What's been ruled out (single-variable on-device experiments)
Wrong .so variant: picker logic in RNLlama.java:190 correctly selects librnllama_jni_v8 for Cortex-A53 (no asimddp) and librnllama_jni_v8_2_dotprod for Cortex-A55. Confirmed live via nativeloader logs in logcat.
Hexagon backend leakage:isHexagonSupported() correctly returns false on Samsung Exynos (no Qualcomm hint, no Snapdragon SOC pattern, no Hexagon codename). HTP libs are extracted to app_rnllama-htp/ but never dlopen'd.
OOM-killed worker thread:no_extra_bufts: true measurably reduces the Android lmkd (lowmemorykiller) cascade severity around the trip from ~8 procs killed/second to ~4 procs killed/second on the A50, but the F5 trip persists. Memory pressure is a contributing factor, not the root cause.
Concurrent JSI calls: duplicate loadPrompt:215 [DEBUG] Input processed log lines from threads in the same millisecond come from llama.cpp's internal multi-threaded prompt batching (n_threads=4), not parallel ctx.completion() calls.
Model-specific bug: three different reasoning models reproduce — qwen3-0.6b-Q4_K_M, tinyllama-1.1b-chat-Q4_K_M, gemma-4-E2B-it-UD-IQ3_XXS.
RAM-tier issue: 3.5 GB (A50) and 5.5 GB (A25 5G) both reproduce.
Is there a known crash on first ctx.completion() on Samsung Exynos that we've missed in the issue tracker? We searched but didn't find a direct match.
Can the <unknown> wrapper be widened to expose the actual .what() for whatever throw path is firing here? PR fix: handle exception in createPromiseTask resultGenerator #318 was supposed to do that for resultGenerator throws — it doesn't seem to apply on this device class. We're happy to test a debug build that adds extra logging at any catch point you suggest.
We haven't tested with n_threads: 1 yet (default appears to be 4 from attachThreadpoolsIfAvailable: Attached ggml threadpool (n_threads=4, n_threads_batch=4)). Worth testing whether this is a llama.cpp threadpool issue specific to multi-core Exynos? Happy to run that experiment if it'd be useful.
I have detailed logs (full per-variant logcat, complete tombstone, /proc/cpuinfo for both devices, build IDs for the loaded .sos) ready to share if any of it would help narrow this. Happy to test patches against the Exynos hardware we have plugged in.
Repro environment
0.12.0-rc.4(cpp pin: buildb8665, commitb863507) and0.12.0-rc.9(cpp pin: buildb8827, commit5e6c0e1). All RC versions in the rc.5 → rc.9 range reproduce the same JS-layer error.s5e8825, Cortex-A78 + A55, ARMv8.2-A + dotprod), 5.5 GB RAMadb install -r. JS bundle served bynpx expo startMetro at runtime (no in-APK bundle).Failure mode
The very first call to
ctx.completion(...)after a successful cold init fails on the JS side with:After this throw, the process stays alive but every subsequent
ctx.completion(...)returns immediately because the consuming app trips a JSI-corruption circuit-breaker (our F5 — see "What's been ruled out" below). Anyctx.completion(...)calls already in flight at the moment of the throw eventually time out at our 30 s wrapper timeout.The
<unknown>is React Native + Hermes' generic fallback wrapper for any C++ throw whose.what()it can't translate (RN #34730). PR #318'sresultGeneratortry/catch does not catch this throw —<unknown>persists in rc.9. Either the throw is not in the resultGenerator path, is notstd::exception-derived, or fires before the resultGenerator is reached.Reproducible steps
npm install llama.rn@^0.12.0-rc.9. Native artifacts download via the rc.5+ postinstall script.initLlamaContextwith these params (matches what we use in production):qwen3-0.6b-Q4_K_M,tinyllama-1.1b-chat-v1.0.Q4_K_M,gemma-4-E2B-it-UD-IQ3_XXS. We've seen the same failure on all three.loadModellogsContext initialized with n_seq_max = 1andattachThreadpoolsIfAvailable: Attached ggml threadpool (n_threads=4, n_threads_batch=4). Latency: ~3.3 s on A50 with TinyLlama 1.1B Q4_K_M, ~10.8 s on A25 with gemma4-e2b-lite IQ3_XXS.ctx.completion({ messages: [{role:'system',content:...},{role:'user',content:...}], n_predict: 512, temperature: 0.1, stop: [...] })with a ~150–250-token prompt.loadPromptlogsInput processed: n_past=N, embd.size=Mfor the prompt batch.[Error: Exception in HostFunction: <unknown>].Expected: completion returns a token stream, OR if it errors, the JS-side error has a meaningful
.what()string.Observed: opaque
<unknown>on every variant tested.Native tombstone
The opaque
<unknown>is the catchable-exception code path. There's a second path that surfaces the underlying abort directly:llama.rn 0.12.0-rc.4+no_extra_bufts: trueproduces a SIGABRT inlm_ggml_abortfromllm_graph_context::build_inp_embd, with the process dying. This is a separate bug specific to rc.4's older cpp pin (b8665/b863507); it is fixed in rc.9's pin (b8827/5e6c0e1). I include it because (a) the abort site gives us the only native stack trace we have for this device/model combination, and (b) the call stack lands inside the samedecode → process_ubatch → build_graph → build_inp_embdpath that the<unknown>variant exits through.The crashing thread is named
mqt_v_native(React Native's mqt JSI worker thread). On the A50 (Cortex-A53, ARMv8.0-A baseline) the picker correctly selectslibrnllama_v8.so(compiled with-march=armv8-a, no extensions) — confirmed viaRNLlama.java:190reading/proc/cpuinfofeaturesfp asimd evtstrm aes pmull sha1 sha2 crc32 cpuid(noasimddp, noi8mm, nofphp, noatomics).Variant matrix (every combination tested on the A50)
no_extra_bufts<unknown>truebuild_inp_embd, process dies<unknown>(then abort)<unknown>true<unknown>A25 5G reproduces variants 1, 3, 4 with the same JS-layer signature (different active model:
gemma4-e2b-lite— confirms the bug is not model-specific).What's been ruled out (single-variable on-device experiments)
.sovariant: picker logic inRNLlama.java:190correctly selectslibrnllama_jni_v8for Cortex-A53 (noasimddp) andlibrnllama_jni_v8_2_dotprodfor Cortex-A55. Confirmed live vianativeloaderlogs inlogcat.isHexagonSupported()correctly returnsfalseon Samsung Exynos (no Qualcomm hint, no Snapdragon SOC pattern, no Hexagon codename). HTP libs are extracted toapp_rnllama-htp/but neverdlopen'd.no_extra_bufts: truemeasurably reduces the Androidlmkd(lowmemorykiller) cascade severity around the trip from ~8 procs killed/second to ~4 procs killed/second on the A50, but the F5 trip persists. Memory pressure is a contributing factor, not the root cause.loadPrompt:215 [DEBUG] Input processedlog lines from threads in the same millisecond come from llama.cpp's internal multi-threaded prompt batching (n_threads=4), not parallelctx.completion()calls.Asks
ctx.completion()on Samsung Exynos that we've missed in the issue tracker? We searched but didn't find a direct match.<unknown>wrapper be widened to expose the actual.what()for whatever throw path is firing here? PR fix: handle exception in createPromiseTask resultGenerator #318 was supposed to do that for resultGenerator throws — it doesn't seem to apply on this device class. We're happy to test a debug build that adds extra logging at any catch point you suggest.n_threads: 1yet (default appears to be 4 fromattachThreadpoolsIfAvailable: Attached ggml threadpool (n_threads=4, n_threads_batch=4)). Worth testing whether this is a llama.cpp threadpool issue specific to multi-core Exynos? Happy to run that experiment if it'd be useful.I have detailed logs (full per-variant logcat, complete tombstone,
/proc/cpuinfofor both devices, build IDs for the loaded.sos) ready to share if any of it would help narrow this. Happy to test patches against the Exynos hardware we have plugged in.