Skip to content

[canary-tpu] grug MoE train crashes: megablox GMM output block (512,32) invalid for (524288,128) #8731

Description

@claude

The TPU canary ferry (Grug MoE smoke test) crashes at compile time on the first train_step. On this run the crash was deterministic across all 4 task attempts, so max_task_failures was exceeded and the job failed.

The expert grouped-matmul fails to lower on TPU v6e: the megablox GMM pallas_call builds an output BlockSpec((512, 32)) for an output array of shape (524288, 128). Pallas requires the last block dim to be divisible by 128 or equal to the array dim; 32 is neither.

ValueError: The Pallas TPU lowering currently requires that the last two
dimensions of your block shape are divisible by 8 and 128 respectively, or be
equal to the respective dimensions of the overall array. Block spec for outputs
in pallas_call kernel at .../pallas/ops/tpu/megablox/gmm.py:393 has block shape
(Blocked(block_size=512), Blocked(block_size=32)), array shape (524288, 128)

Call path: levanter/grug/_moe/scatter.py:48 (expert MLP projection), then haliax.nn.ragged_dot._ragged_dot_megablox_impl (lib/haliax/src/haliax/nn/ragged_dot.py:93), then jax.experimental.pallas.ops.tpu.megablox.gmm. The output n=128 equals the canary model intermediate_dim.

Reproduce:

  1. Run the TPU canary ferry python -m experiments.ferries.canary_ferry on v6e-4 (CANARY_MODEL: hidden_dim=32, intermediate_dim=128, num_experts=32, num_experts_per_token=2, num_layers=4).
  2. The failure is intermittent across scheduled runs (history below); it reproduces when the megablox GMM lowers fresh instead of loading from the shared JAX compilation cache.

Expected: the megablox GMM lowers with a valid output block (last dim divisible by 128, or equal to n) and the canary trains to the regression gate.

Root-cause status (unresolved discrepancy)

The observed tn=32 does not match the checked-out tiling logic. _megablox_tile_size() at HEAD returns an n-tile of 1024, so _ragged_dot_megablox_impl passes tiling[2] = min(n=128, 1024) = 128, which would produce a valid (512, 128) output block. Static reading of the current code does not explain how tn=32 was emitted. This gap is the crux and needs a kernel owner.

Signals pointing away from a fresh code regression on the commit for this run:

  • The prior scheduled canary (2026-08-27 06:30 UTC, f8972ac) succeeded. The three commits since (PR 8721 external eval/RL runtimes, PR 8717 echo dashboard, PR 8710 IAM grant) touch no JAX, haliax, levanter, or grug code.
  • ragged_dot.py last changed 2026-08-25 (PR 8610), before two successful runs.
  • Canary history alternates pass/fail (Aug 26 pass, Aug 27 06:30 pass, 03:42 and 17:21 fail).

The failing attempt logged a compilation-cache read failure immediately before the crash:

UserWarning: Error reading persistent compilation cache entry for
jit__create_lm_example: RuntimeError: Session is closed

Shared cache: gs://marin-eu-west4/tmp/ttl=30d/compilation-cache. Leading hypothesis: a latent megablox tiling incompatibility for these shapes, normally masked by compilation-cache hits and exposed when the cache read fails and the kernel lowers cold. Not confirmed.

Canary run context:

Metadata

Metadata

Assignees

No one assigned

    Labels

    agent-generatedCreated by automation/agentbugSomething isn't workingcanaryCanary ferry / smoke test issues

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions