Skip to content

[Bug]: TensorRT-LLM 1.3.0rc18 Llama-3-70B inference failure on 8x NVIDIA RTX 6000D - #17034

Open
pjdurden wants to merge 2 commits into
NVIDIA:mainfrom
pjdurden:fix/16899
Open

[Bug]: TensorRT-LLM 1.3.0rc18 Llama-3-70B inference failure on 8x NVIDIA RTX 6000D#17034
pjdurden wants to merge 2 commits into
NVIDIA:mainfrom
pjdurden:fix/16899

Conversation

@pjdurden

@pjdurden pjdurden commented Jul 30, 2026

Copy link
Copy Markdown

Issue #16899 — Llama-3-70B BF16 TP>1 fails on 8x RTX 6000D

Upstream: #16899

1. Root cause

The reporter's follow-up (v1.3.0rc22) pins the failure precisely:

AllReduce.__init__
  → get_allreduce_workspace
    → CustomAllReduceHelper.allocate_allreduce_fusion_workspace
      → IpcMemory.__init__
        → IpcMemory.open_ipc_memory
          → cudaIpcOpenMemHandle  ← cudaErrorInvalidDevice (101)
RuntimeError: CUDA Runtime API error: <cudaError_t.cudaErrorInvalidDevice: 101>

and notes that can_access_peer() returned True on the same machine.

can_access_peer() (tensorrt_llm/_ipc_utils.py:39) only calls
cudaDeviceCanAccessPeer. That answers "can GPU A address GPU B's memory",
which is necessary but not sufficient for CUDA IPC: exporting/importing an
IPC memory handle is a separate capability that also fails on GPUs without CUDA
IPC support and when the ranks do not share an IPC namespace. On this system
cudaDeviceCanAccessPeer succeeds while cudaIpcOpenMemHandle does not.

Because can_access_peer() said True, IpcMemory was constructed with
open_ipc=True, and open_ipc_memory() passed the raw CUDA error straight to
_raise_if_error(). That unhandled RuntimeError aborts engine initialization.

This is a hard crash for a situation the runtime already fully supports. The
non-IPC path exists and works: IpcMemory(open_ipc=False) leaves all pointers
null, and on the C++ side AllReduceOp::ifFallbackToNCCL
(cpp/tensorrt_llm/thop/allreduceOp.cpp:1436) already routes every all-reduce to
NCCL when P2P/NVLink is absent — which is exactly this hardware (RTX 6000D is a
PCIe part with no NVLink, so mIsNVLINKSupported is false and the custom kernels
would never have been selected anyway). Only the eager Python-side workspace
allocation stood in the way.

Two secondary observations that match the report:

  • FP8 worked because it fits in fewer bytes, not because it dodges IPC — the
    reporter's FP8 runs used a configuration that did not hit this init path.
  • TRTLLM_DISABLE_CUSTOM_ALLREDUCE=1 had no effect because no such env var
    exists
    anywhere in the tree (grep returns nothing). The supported knob is
    allreduce_strategy: NCCL in the LLM args, which skips the workspace
    allocation entirely — a valid workaround, but users should not need it.

2. The fix

Degrade gracefully to the already-supported NCCL path instead of crashing.

tensorrt_llm/_ipc_utils.pyIpcMemory.open_ipc_memory no longer raises
when IPC handles cannot be exchanged. It now returns None, and
IpcMemory.__init__ reacts by setting open_ipc = False, leaving the pointers
null. Two details matter:

  • Collective agreement. The decision is allgathered across the TP group, so
    either every rank gets IPC buffers or none does. Without this, a rank that
    succeeded would keep using buffers its peers never mapped, and the subsequent
    tp_allgather calls could desync. A dummy handle is exchanged when
    cudaIpcGetMemHandle itself fails, to keep the collectives symmetric.
  • Cleanup. Handles opened before the failure are closed and the local buffer
    is freed, so the fallback does not leak device memory.

cudaMalloc/cudaMemset failures still raise — those are genuine errors, not a
capability gap.

tensorrt_llm/_torch/distributed/allreduce_helper.py — gate
lamport_initialize() on lamport_buffers.open_ipc rather than on
is_p2p_supported. Those two can now disagree, and calling lamport_initialize
with a null local_ptr would be an illegal memory access.

tensorrt_llm/_torch/distributed/ops.py — defense in depth. Added
allreduce_workspace_has_ipc() and, in AllReduce.__init__, downgrade the
strategy to NCCL when the workspace has no IPC buffers. Without this, a machine
that does have NVLink but cannot do IPC would pass
ifFallbackToNCCL on the C++ side and dereference null workspace pointers inside
a custom kernel — trading a clear init-time error for an obscure crash. The
LOWPRECISION workspace allocation moved below this check for the same reason
(initialize_static_lowprecision_buffers on a null workspace).

SYMM_MEM is unaffected: AllReduce.forward tries self.symm_mem_allreduce
before consulting self.strategy, so the downgrade only changes the fallback
path.

3. Files changed

File Change
tensorrt_llm/_ipc_utils.py open_ipc_memory returns Optional[...]; collective IPC-failure agreement + cleanup; IpcMemory.__init__ clears open_ipc; copyright year
tensorrt_llm/_torch/distributed/allreduce_helper.py gate lamport_initialize on the actual IPC state
tensorrt_llm/_torch/distributed/ops.py allreduce_workspace_has_ipc(); strategy downgrade to NCCL; reorder LOWPRECISION allocation
tests/unittest/_torch/distributed/test_ipc_memory_fallback.py new — 3 focused tests, CUDA runtime stubbed (no GPU, no MPI)

4. Risk and uncertainty

  • I could not reproduce on the reporter's hardware. No RTX 6000D, and this
    sandbox has neither GPUs nor torch. The root cause is derived from the
    reporter's traceback plus reading the code; the specific reason
    cudaIpcOpenMemHandle returns 101 on that GPU is not established. The fix is
    deliberately agnostic to that reason — it handles "IPC handles cannot be
    exchanged" however it arises.
  • Behavior change: what used to be a fatal error is now a warning plus a
    slower-but-correct NCCL path. That is the right trade-off here (the runtime
    already treats non-P2P topologies this way), but it does mean a genuinely
    broken IPC setup now degrades silently-ish instead of failing loudly. The
    logger.warning is there to make it visible.
  • Extra collective: one tp_allgather of a bool per IpcMemory
    construction (3 per TP group at init). Negligible, but it is a new
    synchronization point — all ranks must construct IpcMemory collectively,
    which they already did for the handle exchange.
  • Not addressed (out of scope): can_access_peer() still reports True on
    such systems, so self.is_p2p_supported in modeling_deepseekv3.py,
    modeling_glm.py, modeling_qwen3_moe.py etc. remains optimistic. Those are
    MoE-specific fused paths that this Llama issue does not exercise. A complete
    fix would also plumb the IPC verdict into AllReduceOp::setGroupTopology
    (cpp/tensorrt_llm/thop/allreduceOp.cpp) so C++ and Python agree from the
    start; the strategy downgrade above covers the same ground from the Python
    side without touching C++.
  • The reporter's original 0.21 symptom (BF16 fails at concurrency 4 but works at
    concurrency 1) is a different failure than the rc22 init crash addressed
    here. Their rc22 report supersedes it, and that is what this fix targets.

5. How I verified it

Constraints: no GPU and no torch in this environment, so pytest cannot import
tensorrt_llm. The pytest file was not executed. Instead:

  1. Logic verified against the real edited source. A standalone harness
    (/tmp/verify_ipc_fallback.py, not committed) loads the actual
    tensorrt_llm/_ipc_utils.py via importlib with the CUDA runtime,
    Distributed, logger, and Mapping stubbed, and runs the same three
    scenarios as the pytest file. All 14 assertions pass:
    • IPC works → open_ipc=True, non-null pointers, one handle opened.
    • cudaIpcOpenMemHandlecudaErrorInvalidDevice(101) (the reported
      failure) → no exception, open_ipc=False, all pointers null, local
      buffer freed, warning logged.
    • Local rank succeeds but a peer failed → this rank also falls back, opened
      handles closed, buffer freed.
  2. Lint/format, matching this repo's split toolchain:
    • ruff check + ruff format --check clean on _ipc_utils.py,
      allreduce_helper.py, and the new test (non-legacy files).
    • yapf --diff and isort --diff clean on ops.py (listed in
      legacy-files.txt). ruff check --config ruff-legacy.toml ops.py reports
      the same 2 pre-existing findings before and after my change (verified via
      git stash).
  3. python -m py_compile on all four files.

Not verified: end-to-end trtllm-bench --tp 8 on affected hardware, and that
NCCL throughput is acceptable for this workload. Both need the reporter's
machine.

Dev Engineer Review

  • Updated CUDA IPC initialization to fall back to NCCL when handle export or import fails.
  • Coordinated IPC failure state across tensor-parallel ranks.
  • Cleaned up partially opened handles and allocated buffers.
  • Preserved errors for genuine CUDA allocation failures.
  • Gated Lamport initialization on actual IPC availability.
  • Added NCCL fallback when IPC workspace buffers are unavailable.
  • Updated the IPC initialization return type to allow None.
  • No configuration or test-list changes were identified.

QA Engineer Review

Added:

  • test_ipc_memory_is_opened_when_ipc_works
  • test_local_ipc_open_failure_falls_back_instead_of_raising
  • test_peer_ipc_failure_disables_ipc_on_this_rank

These tests are not referenced in test-db/ or qa/ integration test lists. Pytest and end-to-end validation were not performed because GPU and Torch support were unavailable. Static checks, compilation, and a standalone harness passed.

Verdict: needs follow-up for GPU-backed end-to-end validation on RTX 6000D hardware.

Copilot AI review requested due to automatic review settings July 30, 2026 00:10
@pjdurden
pjdurden requested review from a team as code owners July 30, 2026 00:10
@coderabbitai

coderabbitai Bot commented Jul 30, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: 4cc9961c-057e-43d1-8d20-9fc6516e8afe

📥 Commits

Reviewing files that changed from the base of the PR and between 0c410e3 and cf616b2.

📒 Files selected for processing (1)
  • tensorrt_llm/_ipc_utils.py
🚧 Files skipped from review as they are similar to previous changes (1)
  • tensorrt_llm/_ipc_utils.py

Walkthrough

CUDA IPC initialization now coordinates failures across tensor-parallel ranks, cleans up partial resources, and reports unavailable IPC. Allreduce initialization detects unusable IPC workspaces and falls back to NCCL. Unit tests cover success and failure paths.

Changes

CUDA IPC fallback

Layer / File(s) Summary
IPC failure contract and cleanup
tensorrt_llm/_ipc_utils.py
IpcMemory returns an optional result, synchronizes IPC errors across ranks, cleans up opened handles and allocations, and disables IPC when unavailable.
Allreduce workspace fallback
tensorrt_llm/_torch/distributed/ops.py, tensorrt_llm/_torch/distributed/allreduce_helper.py
Allreduce verifies IPC workspace availability, selects NCCL when IPC is unavailable, and gates Lamport initialization on IPC being open.
IPC fallback validation
tests/unittest/_torch/distributed/test_ipc_memory_fallback.py
CUDA and collective fakes test successful IPC setup, local failures, peer failures, pointer clearing, and cleanup.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Suggested reviewers: bowenfu

Sequence Diagram(s)

sequenceDiagram
  participant AllReduce
  participant IpcMemory
  participant TPCollective
  participant NCCL
  AllReduce->>IpcMemory: allocate IPC workspace
  IpcMemory->>TPCollective: exchange IPC availability
  TPCollective-->>IpcMemory: return collective status
  IpcMemory-->>AllReduce: return workspace or unavailable
  AllReduce->>NCCL: select NCCL when IPC is unavailable
Loading
🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 16.67% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly identifies the affected workload, hardware, and initialization failure addressed by the changes.
Description check ✅ Passed The description clearly explains the issue, root cause, solution, risks, changed files, and test limitations.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🧹 Nitpick comments (2)
tests/unittest/_torch/distributed/test_ipc_memory_fallback.py (1)

74-157: 📐 Maintainability & Code Quality | 🔵 Trivial | 🏗️ Heavy lift

Expand coverage for the remaining fallback contracts.

Coverage summary — insufficient. The three added tests cover successful IPC setup, local import failure, and peer-reported failure. CI/QA test-list membership cannot be verified because no tests/integration/test_lists/test-db/ or tests/integration/test_lists/qa/ file was provided.

  • Make FakeCudart.cudaIpcGetMemHandle() configurable and test its new dummy-handle/cleanup path.
  • In test_local_ipc_open_failure_falls_back_instead_of_raising, use FakeDist(TP_SIZE) so a local failure alone drives agreement; peer_ipc_ok=False currently masks failures to propagate the local error.
  • Add mocked coverage that unavailable IPC downgrades AllReduce to NCCL with workspace is None, and that lamport_initialize() is skipped.

As per path instructions, test changes require coverage and test-list status review.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tests/unittest/_torch/distributed/test_ipc_memory_fallback.py` around lines
74 - 157, Expand the IPC fallback tests around FakeCudart.cudaIpcGetMemHandle,
making its result configurable and covering the dummy-handle and cleanup
behavior. Update test_local_ipc_open_failure_falls_back_instead_of_raising to
use FakeDist(TP_SIZE), then add mocked coverage verifying unavailable IPC
downgrades AllReduce to NCCL with workspace=None and skips lamport_initialize();
also review coverage and test-list status for these test changes.

Source: Path instructions

tensorrt_llm/_ipc_utils.py (1)

18-18: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Use built-in generics in open_ipc_memory

Replace Optional[Tuple[List[int], int]] with tuple[list[int], int] | None. List is still needed by the other annotations in this file, so the typing import can’t be dropped yet.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@tensorrt_llm/_ipc_utils.py` at line 18, Update the return annotation of
open_ipc_memory to use the built-in generic form tuple[list[int], int] | None
instead of Optional[Tuple[List[int], int]]. Retain the existing typing imports
because List remains required by other annotations in the file.

Sources: Coding guidelines, Learnings

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@tensorrt_llm/_ipc_utils.py`:
- Around line 161-163: Update the CUDA IPC warning in the logger.warning call to
use the f-string conversion flag for ipc_error instead of explicitly calling
repr(), resolving Ruff RUF010 while preserving the existing message.

---

Nitpick comments:
In `@tensorrt_llm/_ipc_utils.py`:
- Line 18: Update the return annotation of open_ipc_memory to use the built-in
generic form tuple[list[int], int] | None instead of Optional[Tuple[List[int],
int]]. Retain the existing typing imports because List remains required by other
annotations in the file.

In `@tests/unittest/_torch/distributed/test_ipc_memory_fallback.py`:
- Around line 74-157: Expand the IPC fallback tests around
FakeCudart.cudaIpcGetMemHandle, making its result configurable and covering the
dummy-handle and cleanup behavior. Update
test_local_ipc_open_failure_falls_back_instead_of_raising to use
FakeDist(TP_SIZE), then add mocked coverage verifying unavailable IPC downgrades
AllReduce to NCCL with workspace=None and skips lamport_initialize(); also
review coverage and test-list status for these test changes.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Enterprise

Run ID: ab9b4213-0365-4477-a4b7-557a34646fe2

📥 Commits

Reviewing files that changed from the base of the PR and between 960530b and 0c410e3.

📒 Files selected for processing (4)
  • tensorrt_llm/_ipc_utils.py
  • tensorrt_llm/_torch/distributed/allreduce_helper.py
  • tensorrt_llm/_torch/distributed/ops.py
  • tests/unittest/_torch/distributed/test_ipc_memory_fallback.py

Comment thread tensorrt_llm/_ipc_utils.py

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

This PR addresses a Tensor Parallel initialization failure where CUDA IPC handle import/export can fail (e.g., cudaIpcOpenMemHandle returning cudaErrorInvalidDevice) even when cudaDeviceCanAccessPeer reports P2P capability, by degrading to the already-supported NCCL path instead of aborting engine initialization.

Changes:

  • Make IpcMemory.open_ipc_memory() return None on CUDA IPC handle exchange failures (with TP-wide agreement + cleanup), and have IpcMemory.__init__ disable IPC (null pointers) instead of raising.
  • Gate Lamport buffer initialization on lamport_buffers.open_ipc rather than the optimistic P2P check.
  • In AllReduce.__init__, detect workspaces that have no usable IPC buffers and downgrade the strategy to NCCL; add a focused unit test module with stubbed CUDA runtime / distributed collectives.

Reviewed changes

Copilot reviewed 4 out of 4 changed files in this pull request and generated 2 comments.

File Description
tensorrt_llm/_ipc_utils.py Make CUDA IPC failures non-fatal by returning None and cleaning up; disables IPC state in IpcMemory on failure.
tensorrt_llm/_torch/distributed/allreduce_helper.py Prevent lamport_initialize() from running when IPC buffers are not actually available.
tensorrt_llm/_torch/distributed/ops.py Add IPC-workspace detection and force NCCL fallback when IPC buffers are missing; reorder LOWPRECISION allocation to avoid null-workspace usage.
tests/unittest/_torch/distributed/test_ipc_memory_fallback.py Add stubbed unit tests covering IPC-success, local IPC-open failure, and peer IPC failure agreement behavior.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment on lines 781 to +785
self.workspace = get_allreduce_workspace(self.mapping)
# Every custom all-reduce kernel reads the peers' IPC buffers. When
# CUDA IPC is unavailable those pointers are null, so NCCL is the
# only strategy that can run. See allreduce_workspace_has_ipc.
if not allreduce_workspace_has_ipc(self.mapping):
Comment on lines +159 to +163
if not all(dist.tp_allgather(ipc_error is None)):
if ipc_error is not None:
logger.warning(
f"CUDA IPC is not usable on this system: {repr(ipc_error)}. "
"Custom all-reduce kernels are disabled, falling back to NCCL."
Ruff RUF010. The warning was reported on the IPC fallback log line added
by this PR. The repo's ruff hook runs --fix over the whole file rather
than changed lines only, so the two pre-existing occurrences in
_raise_if_error are converted in the same pass to keep
ruff --fix --exit-non-zero-on-fix clean.

Signed-off-by: pjdurden <prajjwalchittori1@gmail.com>
@pjdurden

Copy link
Copy Markdown
Author

@BowenFu @schetlur-nv @JunyiXu-nv @allisonlim-nv could one of you take a look when you get a chance?

The reporter of #16899 applied the patch on 8x RTX 5090 (PCIe, no NVLink), a different platform from the RTX 6000D in the original report:

Before: crash at engine init, cudaErrorInvalidDevice: 101 from cudaIpcOpenMemHandle, all 8 ranks.
After: falls back to NCCL, engine initializes, warmup and a 100 request benchmark run to completion.

So the failure is not specific to the RTX 6000D, it affects PCIe-only parts generally.

Also pushed one more commit for Ruff nit.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants