Skip to content

Inductor fails to compile cumsum on a 0-dim constant tensor with tuple index out of range

Compilation aborts on a graph containing a cumulative sum over a scalar constant. A post-graph optimisation recognises the pattern, asks the tensor for the length of the dimension being summed, and a scalar has no dimensions to ask about, so indexing its empty shape raises before any code is generated.

Quick answer

An optimisation asks a scalar for the size of a dimension it does not have. Give the constant an explicit dimension, or drop the cumulative sum over a scalar, which is the identity anyway.

Compilation#inductor#torch-compile#cumsum#zero-dim#scalar-tensor#post-grad

What this failure is

A compile-time failure in which a post-graph optimisation for cumulative sum reads a dimension length from the shape of a 0-dimensional constant tensor, whose shape tuple is empty, raising an index error before code generation.

Why it happens (the mechanism)

Pattern-based rewrites match on operation and operands and then assume the shapes they usually see. A cumulative sum is almost always applied to something with at least one dimension, so reading the length of that dimension is a safe-looking step. A scalar constant satisfies the match and violates the assumption, and the guard for the degenerate case was never written because the degenerate case is not useful enough for anyone to have tried it.

What you'll observe

  • The same function runs correctly without compilation, so the model is not wrong
  • The error is an index error inside the compiler, which says nothing about which operation caused it
  • It depends on the tensor being a compile-time constant, so an identical shape produced at runtime does not trigger it
  • A scalar cumulative sum looks pointless and is usually incidental to a larger expression nobody wrote deliberately

Common symptoms and what they mean

SymptomWhy it happens
InductorError: IndexError: tuple index out of range raised at compile timeThe optimisation exists because a cumulative sum along a dimension of known length can often be replaced with something cheaper. To do that it needs the length, which it reads by indexing the shape tuple with the dimension number. A scalar's shape tuple is empty, so any index into it is out of range.
The traceback passing through a post-graph pattern replacement for cumulative sum and a line computing a dimension size from a shapeThe pattern only fires for a constant because the rewrite needs the shape at compile time. A tensor of the same rank produced at runtime keeps its size unknown, the precondition fails, and the optimisation declines to apply — which is why an apparently identical program compiles.
A suggestion to set a verbose environment variable for the internal stack traceThe rewrite is an optimisation, not a requirement. Nothing about generating code for this graph needs it, which is why the failure feels arbitrary: the compiler is stopping in a step whose entire purpose is to make the result faster.
The same code compiling successfully with the graph-capture-only backends and failing only with the default oneThe optimisation exists because a cumulative sum along a dimension of known length can often be replaced with something cheaper. To do that it needs the length, which it reads by indexing the shape tuple with the dimension number. A scalar's shape tuple is empty, so any index into it is out of range.

Which systems are affected

  • Compiled graphs containing a cumulative sum over a tensor built from a literal, such as a scalar constant
  • The post-graph optimisation stage, which rewrites recognised patterns after the graph is captured and before code generation
  • Code where a scalar reaches an operation expecting at least one dimension through broadcasting or a generic helper
  • Model code generated or templated rather than written by hand, where a degenerate shape is easy to produce unnoticed

How to confirm this is the problem

Apply the following checklist to a small reproduction: each box below is a positive signal that you are looking at this exact failure rather than a sibling in the same taxonomy.

  • Compile with a graph-capture-only backend. Succeeding there while the default backend fails places the fault in code generation rather than in tracing.
  • Reduce the function to a cumulative sum over a single scalar constant. Reproducing on that alone identifies the pattern precisely.
  • Add an explicit dimension to the constant and recompile. Success confirms the empty shape tuple as the cause.

Example training logs (fingerprint)

training.log (synthetic fingerprint)
torch._inductor.exc.InductorError: IndexError: tuple index out of range
  File ".../torch/_inductor/fx_passes/post_grad.py", in pointless_cumsum_replacement
    dim_size = shape[dim]
  File ".../torch/_inductor/compile_fx.py", in _compile_fx_inner
Set TORCHDYNAMO_VERBOSE=1 for the internal stack trace

Timestamps and exact values vary across runs, but the pattern. An info-level start, an early WARN, an ERROR carrying the symptom. Is the actual fingerprint you should alert on. The Denpex platform flags this combination automatically.

Root cause, fix & prevention, signed in

Sign up free to see why this failure really happens, the exact remediation steps, and the production-grade prevention pattern. You also get 3 free full diagnoses for your own training logs.

Sign up free. Unlock the full analysis

No credit card · 3 free diagnoses · Instant access

Why the recommended fix works

Adding a dimension makes the shape tuple non-empty, so the length is readable and the rewrite proceeds normally. Removing the operation avoids the pattern entirely. Computing the constant at runtime leaves its size unknown, and the rewrite requires a compile-time size, so it declines to fire. Each removes the precondition rather than working around the symptom.

Code examples

typical reference pattern
// Typical pattern:
import torch.optim as optim
optimizer = optim.AdamW(model.parameters(), lr=3e-4)
scheduler = optim.lr_scheduler.CosineAnnealingLR(optimizer, T_max=num_training_steps)
for step in range(num_training_steps):
    optimizer.zero_grad()
    loss = model(batch)
    loss.backward()
    optimizer.step()
    scheduler.step()

Adapt the snippet to your framework. The same pattern holds for PyTorch Lightning, Hugging Face Trainer, DeepSpeed, Megatron-LM, and vLLM training wrappers. Where the wrapper exposes a config flag (for examplelr_scheduler_type in Trainer), prefer the flag over the imperative API to keep the schedule declarative and reproducible.

Best practices by model family

Model / StackRecommendationNotes
Scalar constant reaches cumsumAdd an explicit dimensionValues unchanged; shape tuple gains an entry.
Operation is incidentalRemove itCumulative sum over a scalar is the identity.
DiagnosingTry a capture-only backendSeparates tracing from code generation in one run.
Cannot change the modelExclude the region from compilationKeeps the rest of the model compiled.

With the fix vs without the fix

DimensionWith the fixWithout the fix
Why eager worksNo pattern rewriting happensRead as a model correctness problem
Why a constant specificallyThe rewrite needs a compile-time sizeExpected to depend on rank alone
What the failing step doesAn optimisation, not a requirementAssumed necessary for code generation

Real engineering notes

The instructive part is the ablation in the upstream report: the graph-capture backends compile it and only the code-generating one fails. That single check separates a tracing problem from a code-generation problem, and it takes one line. Make it the first thing you do with any compiler error whose traceback lands in framework internals — it halves the search before you have read a single frame properly.

Visual fingerprint

An empty shape tuple has no index to read
  torch.tensor([5.0])   shape (1,)   ->  shape[0] = 1     rewrite proceeds
  torch.tensor(5.0)     shape ()     ->  shape[0] = ???   IndexError

  torch.rand(())        shape ()     ->  size unknown at compile time
                                          precondition fails, rewrite skipped
The rewrite reads the length of the summed dimension from the shape tuple. A one-element vector has one to read and a scalar has none. A runtime-produced scalar escapes because the rewrite needs the size at compile time and declines to fire without it.

Root cause, fix & prevention

Frequently asked questions

Twelve targeted questions that engineers and on-call staff most commonly ask about this failure.

Is my model wrong?
No. It runs correctly in eager mode. The failure is in an optimisation applied after the graph is captured.
Why does a runtime-produced scalar not fail?
The rewrite needs the dimension size at compile time. A runtime tensor's size is unknown then, so the optimisation does not apply.
Do I lose performance by adding a dimension?
No meaningfully. A length-one vector carries the same single value and lets the rewrite proceed normally.
How do I tell tracing from code generation?
Compile with a capture-only backend. Success there points at code generation; failure points at capture.

Don't just read the fix, diagnose your run

The encyclopedia tells you what went wrong. Denpex tells you what went wrong in YOUR training run. With your logs, your config, and your stack.