Treat an empty LLM data path as absent - #21835
Conversation
LlmModuleConfig's builder defaulted dataPath to "", and create_text_llm_runner takes any value the optional holds -- including the empty string -- so an Android caller that never asked for a data file still reached the loader with "". The load then failed on `Failed to open : No such file or directory`, and because LlmModule.load() surfaces that through a native exception, the process aborted rather than returning the error. The builder default is now null, and both runner factories ignore an empty path, so a caller in any language whose "no value" is an empty string lands on the no-data-file branch instead of a failed mmap.
🔗 Helpful Links🧪 See artifacts and rendered test results at hud.pytorch.org/pr/pytorch/executorch/21835
Note: Links to docs will display an error until the docs builds have been completed. ✅ No FailuresAs of commit fd3ef18 with merge base abc5586 ( This comment was automatically generated by Dr. CI and updates every 15 minutes. |
This PR needs a
|
|
I'm curious overall, how was your experience exporting and running the lfm2.5 family? |
|
Thanks for merging it, and for asking — the export side was genuinely smooth, so the honest answer is that everything that cost me time was downstream of it.
Three things did cost time, in the order I hit them. The empty The second was the tokenizer. LFM2.5's The third I could not close, and it is the one I would most like a second opinion on: generation degenerates through One smaller note while I have your attention: |
LlmModuleConfig.BuilderdefaultsdataPathto"", andcreate_text_llm_runnertakes whatever the optional holds — the empty string included. So an Android caller that never asked for a separate data file still reaches the loader with"":Because
LlmModule.load()reports failure throughthrowExecutorchException, this comes out asFatal signal 6 (SIGABRT)with abort message'ptr'and the process dies, rather than an error the app can catch. That is what makes it hard to place: nothing in the message points at a data path, and the same.pteand tokenizer work everywhere else.Reproducing it takes only the documented builder call — no data file, no unusual model:
Passing
.dataPath(null)explicitly is the workaround.The change
The builder default becomes
null, and both runner factories ignore an empty path so a caller in any language whose "no value" is an empty string lands on the no-data-file branch rather than a failed mmap. Three files: the Kotlin default, the twodata_path.has_value()checks, and a test for the default.I kept the guard in C++ as well as fixing the Kotlin default, because the optional is public API and an empty string will keep arriving from bindings that have no null.
Verification
On a Pixel 8a, arm64, LFM2.5-350M exported to XNNPACK 8da4w:
load(), every timeThose rates are low because the phone was locked while measuring, which parks the app on the little cluster — not a property of this change.
I also confirmed the failure is not the model or the tokenizer: the
.pteexposes the full metadata method set the runner expects, and the tokenizer loads and encodes identically on the same device through a standalone harness.This PR was authored with Claude.