fix(AMD): retry ROCm llama-server with bundled HIP on HIP/ROCR mismatch - #9002
Conversation
System ROCm on LD_LIBRARY_PATH plus bundled libamdhip64.so dies on hsa_amd_queue_create@ROCR_1 (exit 127). That is not a VRAM miss, so do not flip --fit; retry once without the system prepend instead. Co-authored-by: Cursor <cursoragent@cursor.com>
for more information, see https://pre-commit.ci
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 50e59148f9
ℹ️ About Codex in GitHub
Codex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback".
There was a problem hiding this comment.
Preserve fit recovery after the ROCm retry
When a host has the HIP/ROCR mismatch and the bundle-only launch then fails for an independent fit-related reason, this continue consumes the second and final iteration of for _spawn_attempt in (0, 1). The subsequent failure therefore cannot take either existing --fit on or --fit off recovery because both are gated on _spawn_attempt == 0, so models affected by both conditions now fail even though each condition has a supported retry. Give the ROCm environment correction its own retry state/budget so the corrected launch can still enter the fit recovery logic if it produces a different failure.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in a5b9458: ROCm and fit recovery now have independent bounded retry state, with behavioral coverage for mismatch -> bundled HIP -> fit-adjusted success.
|
@codex review |
|
Codex Review: Didn't find any major issues. Hooray! Reviewed commit: ℹ️ About Codex in GitHubCodex has been enabled to automatically review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. When you sign up for Codex through ChatGPT, Codex can also answer questions or update the PR, like "@codex address that feedback". |
|
This fixes the ROCm library mismatch, but it shares the same two-attempt budget as the existing --fit retry. Could we give the ROCm fallback its own retry and add a behavioral test for that sequence? |
The library-mix retry was sharing the (0, 1) slot with --fit, so a later VRAM crash after bundled HIP could not fit-retry. Keep the two recoveries independent and pin the mix → bundled HIP → --fit on sequence. Co-authored-by: Cursor <cursoragent@cursor.com>
for more information, see https://pre-commit.ci
|
@Etherll Agreed. The ROCm env retry was sharing the same (0, 1) slot as Pushed a follow-up: ROCm and --fit each gets their own retry (three launches max). Also added a behavioural test for mix → bundled HIP → |
|
@codex review |
for more information, see https://pre-commit.ci
|
@codex review |
|
Codex Review: Didn't find any major issues. What shall we delve into next? Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
for more information, see https://pre-commit.ci
unslothai#7233 prepends the whole system ROCm dir, so any lib in it can be the one that fails to resolve, not just HIP. Parse glibc's ``symbol lookup error: <object>: undefined symbol: <sym>`` line and match the ROCm family on the object's basename, so librocblas / libhipblaslt reach the same bundle-only retry instead of the generic 127 text plus a --fit retry that cannot load a missing symbol. The message now names the object and symbol the loader reported: it hardcoded hsa_amd_queue_create while the predicate also fired on any rocr_ match. _run_full_offload_spawns recorded every subprocess.Popen, so a host with the rocm_sdk wheel installed also captured the offload-arch probe that discover_current_target_family() shells out to from inside load_model. That landed at index 0 and shifted the assertions onto the wrong process, which is why the test passed in a full-file run and failed on its own. Record only the fake server and delegate the rest. Also guard the empty LD_LIBRARY_PATH case, cover the mix the retry does not fix (both fit-branch guards had no test), and drop three source-text assertions the behavioural tests already prove.
|
@indrajeetapache thank you so much for building out this fix, am pushing my review changes, cleaned up a few tests and methods |
|
Reviewed at I pushed four corrections plus a merge of latest main to your branch rather than asking you to round-trip. The first one does not show up in a normal run, and your test plan does not include 1. The behavioral test asserted against the wrong process. That lands at index 0, shifts everything, and eats 2. The detector could not match the other object this failure has. 3. The message named a symbol the predicate did not require. It hardcoded 4. Small one: I also added a test for when bundle-only does not help. Both Two things still open that are not code review. No workflow has run here, so pre-commit is the only green check. And the hardware line in your test plan is unchecked, so nobody has run this on a host that reproduces the crash. Worth asking the reporter, since the premise is one line on their machine: if For the record, two follow-ups, not asks for this PR: the durable fix is comparing system ROCm against the bundle's version before launching rather than after a crash, and the correction does not outlive the load, so the STT sidecar builds the same env with no retry and stays broken on this host. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 0fcbfa3a7f
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
Both captures are runs of non-whitespace taken straight from the child, and _drain_stdout keeps an unterminated line whole, so a wrapper on LLAMA_SERVER_PATH printing a megabyte-scale token would put all of it in the API error. _short is the existing bound for exactly this and every other capture in _classify_start_failure_text already uses it.
Twelve issue citations across four files for one fix, several of them restating the line below. Keep unslothai#8998 where it identifies the crash signature and unslothai#7233 where it names the prepend being undone, drop the rest, and cut the prose that repeated the code. One was already stale: the ggml test said the ROCm branch "names libamdhip64", which stopped being true when the detector started matching the whole ROCm family.
|
Confirmed and fixed in Also pushed a comment pass in 213 pass, ruff clean, formatted with the pinned ruff 0.6.9. |
The retry repaired one launch and recorded nothing, so each load paid another crashing spawn, and the STT sidecar never recovered at all: it builds its child env through the same _llama_server_env_for_binary and has no retry of its own, leaving dictation broken on the host chat had just been fixed on. Record the build dir once a bundle-only launch reaches health and skip the prepend there from then on. Written only from that proof, so a host the unslothai#7233 prepend is right for cannot land in it, and kept in-process so a ROCm or driver upgrade re-tests on restart.
|
Follow-up in The problem it fixes. The retry repaired one launch and recorded nothing. So on a host that hits the mix, every load rebuilt the prepend and crashed into it again before recovering, and the STT sidecar never recovered at all: The fix. The build dir is recorded once a bundle-only launch reaches health, and the prepend is skipped there from then on. The sidecar needed no code of its own: it calls the same helper, so it inherits the correction. Three constraints keep it safe. Written only from that proof, so a host the Tests cover both directions, including that a retry which does not fix the crash leaves no record, and the harness resets the state per run so a correction cannot leak between tests. Still unverified on real hardware, same as the rest of the PR. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 6d9e83c662
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
glibc echoes the object verbatim, so a custom LLAMA_SERVER_PATH under a directory with spaces kept them and \S+? stopped at the first one, dropping the crash into the generic 127 text instead of the bundle-only retry. Split on the ": undefined symbol:" that follows the object, the same way the missing-library branch above already keeps "/opt/My Runtime/libfoo.so" whole. Bounded at PATH_MAX rather than to end of line: no longer path can exist, and an unbounded lazy capture let one hostile line drive the scan quadratically (200 prefixes on a 400 KB line went 3ms -> 803ms; with the bound, 16ms).
|
Confirmed and fixed in One thing worth recording, since the obvious fix has a cost. Widening Tests: a bundle under |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: f525e59ccd
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
…he first _did_rocm_retry lived in _spawn_and_wait, but the correction edits the shared env, so it outlives that call: the drafterless, no-flash, arch-fallback and CPU replay paths all call _spawn_and_wait again with the corrected env and a fresh flag. A load that recovered through one of those came up healthy without recording anything, and the STT sidecar kept the prepend it had already proved wrong. Hoist the flag to the load scope. Verified both ways: mix, signal crash, fit retry, then a healthy no-flash launch now records; with the per-call flag that same sequence does not.
|
Confirmed and fixed in Reproduced before fixing. Driving Worth noting the flag only widens where a success is recorded. It cannot cause a second correction, since the retry is still guarded on the rebuilt |
|
Confirmed this hits the path in studio/backend/core/inference/llama_cpp.py where the system ROCm prepend is applied unconditionally and the resulting 127 is then treated as a VRAM problem and sent into the --fit retries. Will get this reviewed. |
|
@codex review |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 8a202181a4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| @staticmethod | ||
| def _remember_bundle_only_rocm(binary: str) -> None: | ||
| with LlamaCppBackend._bundle_only_rocm_lock: | ||
| LlamaCppBackend._bundle_only_rocm_dirs.add(str(_llama_lib_dir(binary))) |
There was a problem hiding this comment.
Invalidate the bundle-only preference after runtime updates
When an in-app unsloth studio update or backend switch replaces llama-server and its libraries in place, this cache entry survives because it is keyed only by the unchanged build directory. A later ROCm load therefore skips the system prepend without testing the new bundle, even though the replacement bundle may again require system ROCm to avoid the hsa_init()/driver mismatch that motivated the default prepend; this recovery can only remove the prepend, so it cannot recover in the opposite direction. Include the binary/runtime revision in the key or clear this preference when the install is replaced.
Useful? React with 👍 / 👎.
There was a problem hiding this comment.
Fixed in b4ee47b: the cache now pairs each resolved install directory with the binary inode/size/mtime revision and invalidates after a same-path runtime replacement. A regression test atomically replaces llama-server and proves the system ROCm prepend is retried. The fingerprint uses Python stat fields documented at https://docs.python.org/3/library/os.html#os.stat_result.
|
@codex review |
1 similar comment
|
@codex review |
|
Codex Review: Didn't find any major issues. Already looking forward to the next diff. Reviewed commit: ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
If Codex has suggestions, it will comment; otherwise it will react with 👍. Codex can also answer questions or update the PR. Try commenting "@codex address that feedback". |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: 51169057be
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # The prepend covers the whole system ROCm dir, so any lib in it can be the | ||
| # one that fails to resolve, not just HIP. Matched on the basename so a path | ||
| # component (/opt/librocm-custom/...) cannot stand in for the object. | ||
| _ROCM_OBJECT_HINTS = ("libamdhip", "libamd_comgr", "libhsa-runtime", "libhip", "libroc") |
There was a problem hiding this comment.
Restrict the ROCm prefix to actual ROCm objects
On native-Linux AMD launches using a custom LLAMA_SERVER_PATH, an unrelated lookup failure from a library such as librocksdb.so is classified as a HIP/ROCR mismatch because librocksdb starts with the overly broad libroc hint. This triggers an irrelevant bundle-only retry, suppresses the existing fit recovery while the same diagnostic persists, and ultimately tells the user to change the ROCm backend instead of fixing the custom library. Match explicit ROCm library families (for example, librocblas/libroctracer/librocm_*) rather than every basename beginning with libroc.
Useful? React with 👍 / 👎.
|
@codex review |
1 similar comment
|
@codex review |

Summary
Fixes the ROCm load failure in #8998.
Studio prepends system ROCm on
LD_LIBRARY_PATHso bundled HIP matches the host amdkfd driver (#7233). On this host that mix still binds bundledlibamdhip64.so.7, which then looks uphsa_amd_queue_create@ROCR_1in a differentlibhsa-runtime64and llama-server exits 127. Vulkan works. A terminalllama-serverfrom~/.unslothworks, because it does not get that prepend.Studio then treated the 127 as an optimistic VRAM estimate and retried with
--fit on, which cannot load a missing ROCr symbol.What this PR does
libamdhip64+hsa_amd_queue_create/ROCR_1)use_system_rocm=False(same asUNSLOTH_LLAMA_NO_SYSTEM_ROCM=1/ a shell launch)--fit on/--fit offretries for this crashDefault launch still prepends system ROCm (#7233). WSL's
librocdxgprepend is unchanged.Test plan
cd studio/backend && python -m pytest tests/test_llama_cpp_start_failure_classification.py tests/test_llama_cpp_darwin_loader_env.py -q --tb=shorthsa_amd_queue_create, version ROCR_1, load a GGUF with the ROCm backend and confirm the bundled-HIP retry starts the serverUNSLOTH_LLAMA_NO_SYSTEM_ROCM=1still skips the system prepend on the first attemptMade with Cursor