pytorch - 💡(How to fix) Fix `torch.compile` guard evaluation crash: `eval_is_non_overlapping_and_dense()` calling convention mismatch with `IsNonOverlappingAndDenseIndicator` [1 comments, 2 participants]

Official PRs (…)
ON THIS PAGE

Recommended Tools

×6

Utilities matched from this issue’s tags and category — try them while you read without losing context.

GitHub issue graph ai analysis

Paste a GitHub issue URL. We fetch that issue, discover linked issues from bodies/comments/timeline, collect linked pull requests, and produce a structured English report.

The report is written in English Markdown for sharing and archival.

Helpful · Quick feedback

Loading…
GitHub stats
pytorch/pytorch#179026Fetched 2026-04-08 02:21:52
View on GitHub
Comments
1
Participants
2
Timeline
37
Reactions
0
Assignees
Timeline (top)
mentioned ×12subscribed ×12labeled ×6assigned ×2

Error Message

AssertionError: Guard check failed: 0/3: eval_is_non_overlapping_and_dense() takes 2 positional arguments but 8 were given

Root Cause

There is a calling convention mismatch between how IsNonOverlappingAndDenseIndicator guard expressions are printed and how they are evaluated:

  1. IsNonOverlappingAndDenseIndicator (SymPy Function in torch/fx/experimental/sym_node.py) takes flat positional args for an N-D tensor — e.g. (s0, s1, s2, s3, st0, st1, st2, st3) for a 4-D tensor.
  2. eval_is_non_overlapping_and_dense (in torch/fx/experimental/symbolic_shapes.py) expects two sequence args: (sizes: Sequence, strides: Sequence).
  3. SYMPY_INTERP maps the string "IsNonOverlappingAndDenseIndicator" directly to eval_is_non_overlapping_and_dense, so when the guard expression is eval()'d at runtime, 8 flat args are passed into a function expecting 2 sequences.
  4. PythonPrinter has no custom _print_IsNonOverlappingAndDenseIndicator method, so SymPy's default printer emits the flat-arg form, triggering the mismatch.

Code Example

AssertionError: Guard check failed: 0/3: eval_is_non_overlapping_and_dense() takes 2 positional arguments but 8 were given

---

# In torch/fx/experimental/symbolic_shapes.py

def _eval_is_non_overlapping_and_dense_interp(*args: int) -> int:
    dim = len(args) // 2
    return eval_is_non_overlapping_and_dense(list(args[:dim]), list(args[dim:]))

SYMPY_INTERP["IsNonOverlappingAndDenseIndicator"] = (
    _eval_is_non_overlapping_and_dense_interp
)

---

import torch

# repeat_kv pattern that triggers the bug under dynamic shapes
def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
    batch, num_key_value_heads, slen, head_dim = hidden_states.shape
    if n_rep == 1:
        return hidden_states
    hidden_states = hidden_states[:, :, None, :, :].expand(
        batch, num_key_value_heads, n_rep, slen, head_dim
    )
    return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)

compiled_fn = torch.compile(repeat_kv, dynamic=True)

x = torch.randn(2, 4, 128, 64)
# First call traces successfully
out1 = compiled_fn(x, 2)
# Subsequent call with different dynamic shape may trigger guard re-evaluation crash
x2 = torch.randn(3, 4, 256, 64)
out2 = compiled_fn(x2, 2)
RAW_BUFFERClick to expand / collapse

🐛 Describe the bug

torch.compile crashes during guard evaluation with:

AssertionError: Guard check failed: 0/3: eval_is_non_overlapping_and_dense() takes 2 positional arguments but 8 were given

Root Cause

There is a calling convention mismatch between how IsNonOverlappingAndDenseIndicator guard expressions are printed and how they are evaluated:

  1. IsNonOverlappingAndDenseIndicator (SymPy Function in torch/fx/experimental/sym_node.py) takes flat positional args for an N-D tensor — e.g. (s0, s1, s2, s3, st0, st1, st2, st3) for a 4-D tensor.
  2. eval_is_non_overlapping_and_dense (in torch/fx/experimental/symbolic_shapes.py) expects two sequence args: (sizes: Sequence, strides: Sequence).
  3. SYMPY_INTERP maps the string "IsNonOverlappingAndDenseIndicator" directly to eval_is_non_overlapping_and_dense, so when the guard expression is eval()'d at runtime, 8 flat args are passed into a function expecting 2 sequences.
  4. PythonPrinter has no custom _print_IsNonOverlappingAndDenseIndicator method, so SymPy's default printer emits the flat-arg form, triggering the mismatch.

When this triggers

This is a latent bug that rarely surfaces. It triggers when all three conditions are met:

  • A tensor has symbolic (dynamic) sizes/strides
  • The tensor layout cannot be resolved statically as contiguous
  • The IsNonOverlappingAndDenseIndicator expression survives into the guard

In our case, this was triggered by a repeat_kv pattern using .expand() followed by .reshape() on a tensor with dynamic shapes under torch.compile.

Suggested Fix

Add a wrapper in SYMPY_INTERP that splits the flat args into two sequences before forwarding to eval_is_non_overlapping_and_dense:

# In torch/fx/experimental/symbolic_shapes.py

def _eval_is_non_overlapping_and_dense_interp(*args: int) -> int:
    dim = len(args) // 2
    return eval_is_non_overlapping_and_dense(list(args[:dim]), list(args[dim:]))

SYMPY_INTERP["IsNonOverlappingAndDenseIndicator"] = (
    _eval_is_non_overlapping_and_dense_interp
)

Alternatively, PythonPrinter could be taught to emit a call that packs the flat args into two lists before calling the function.

Minimal Reproducer

import torch

# repeat_kv pattern that triggers the bug under dynamic shapes
def repeat_kv(hidden_states: torch.Tensor, n_rep: int) -> torch.Tensor:
    batch, num_key_value_heads, slen, head_dim = hidden_states.shape
    if n_rep == 1:
        return hidden_states
    hidden_states = hidden_states[:, :, None, :, :].expand(
        batch, num_key_value_heads, n_rep, slen, head_dim
    )
    return hidden_states.reshape(batch, num_key_value_heads * n_rep, slen, head_dim)

compiled_fn = torch.compile(repeat_kv, dynamic=True)

x = torch.randn(2, 4, 128, 64)
# First call traces successfully
out1 = compiled_fn(x, 2)
# Subsequent call with different dynamic shape may trigger guard re-evaluation crash
x2 = torch.randn(3, 4, 256, 64)
out2 = compiled_fn(x2, 2)

(Note: exact repro may depend on PyTorch version and how dynamic shapes interact with the guard system. The bug was observed on PyTorch 2.8.0.)

Versions

  • PyTorch version: 2.8.0
  • Python version: 3.12
  • OS: Linux

cc @chauhang @penguinwu @ezyang @bobrenjc93 @aditvenk @laithsakka

extent analysis

TL;DR

The most likely fix is to add a wrapper in SYMPY_INTERP that splits the flat args into two sequences before forwarding to eval_is_non_overlapping_and_dense.

Guidance

  • Implement the suggested fix by adding a wrapper function _eval_is_non_overlapping_and_dense_interp in torch/fx/experimental/symbolic_shapes.py to handle the argument mismatch.
  • Verify the fix by running the minimal reproducer provided and checking that it no longer crashes with an AssertionError.
  • Consider teaching PythonPrinter to emit a call that packs the flat args into two lists before calling the function as an alternative solution.
  • Be aware that the exact reproducibility of the bug may depend on the PyTorch version and how dynamic shapes interact with the guard system.

Example

The suggested fix can be implemented as follows:

def _eval_is_non_overlapping_and_dense_interp(*args: int) -> int:
    dim = len(args) // 2
    return eval_is_non_overlapping_and_dense(list(args[:dim]), list(args[dim:]))

SYMPY_INTERP["IsNonOverlappingAndDenseIndicator"] = (
    _eval_is_non_overlapping_and_dense_interp
)

Notes

The bug is latent and rarely surfaces, requiring specific conditions to be met, including symbolic tensor sizes/strides, inability to resolve the tensor layout statically as contiguous, and the IsNonOverlappingAndDenseIndicator expression surviving into the guard.

Recommendation

Apply the suggested workaround by adding the wrapper function to SYMPY_INTERP to fix the argument mismatch and prevent the AssertionError crash. This solution directly addresses the root cause of the issue.

Vote matrix · Quick signals

Works
Did the solution work? Tap to confirm.
Easy Fix
Was it a quick fix?
Time Saver
Did it save you time?
Blocking
Was it severely blocking?
Common Issue
Are others likely hitting this too?
Flaky / Intermittent
Is it intermittent?
Verified / Reproducible
Can you reproduce it reliably?
Loading…

Still need to ship something?

×6

Another batch ranked right after the header list — different links, same matching logic.

Back to top recommendations

TRENDING