deepspeedai / deepspeedai/DeepSpeed

ZeRO skips the optimizer step on gradient overflow without logging anything

Open
#8,415 0 comments 1 reaction 1 assignee View on GitHub

@sfc-gh-abkulkarni is already working on this.

Since Sep 6, 2026.

Dominant language
Python
Stars
43.1k
Forks
5k
Avg merge
4d 15h
Merged PRs (30d)
112

Description

Repo: deepspeedai/DeepSpeed
Version: DeepSpeed 0.19.2

Problem

When ZeRO-1/2 detects a non-finite gradient it clears gradients and returns early from step()
(deepspeed/runtime/zero/stage_1_and_2.py:2154):

self._update_scale(self.overflow)
if self.overflow:
    see_memory_usage('After overflow before clearing gradients')
    self.zero_grad(set_to_none=True)
    ...
    return

Nothing is logged. Under fp16 this is usually survivable because _update_scale reports the loss
scale changing, which gives an operator an indirect signal. Under bf16 the loss scale is fixed,
so there is no output at all: engine.step() returns normally, the training loop advances, the LR
scheduler steps, metrics are emitted, and the run looks completely healthy while no weights are
ever updated
.

DynamicLossScaler does log OVERFLOW! ... Skipping step, but CreateLossScaler only returns it
for fp16 + dynamic_scaling; bf16 always gets the static LossScaler, which logs nothing. The
engine does count the skip in engine.skipped_steps, but that is only surfaced by
_report_progress, which is gated on steps_per_print — and STEPS_PER_PRINT_DEFAULT is None,
so on a default config it never fires.

In a bf16 RL run this meant every optimizer step was skipped with no indication. The only
observable symptom was that weights synced downstream were byte-identical across steps; loss and
reward curves stayed plausible throughout, since reward noise readily hides a frozen policy.

Related

check_grad_overflow defaults to False for bf16 while fp16 forces it True
(deepspeed/runtime/precision_config.py). That default is defensible — bf16's range makes
overflow rare — but the combination is a sharp edge: with the check off, an Inf gradient flows
into the optimizer and writes NaN into every trainable tensor; with it on, steps are skipped
silently. Both failure modes are invisible. Documenting the flag's interaction with bf16 would
help even if the default stays.

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.