[launch] Report one actionable diagnostic when distributed ranks fail (and make --quiet work for multi-GPU) - #4139
Open
mosafariuk wants to merge 2 commits into
Open
Conversation
Classify ChildFailedError per-rank failures into watchdog-abort / agent-teardown / python-exit buckets, and render one bordered, actionable diagnostic (first-observed-failure labelled as torchelastic's earliest-timestamp heuristic, victim-rank caveat, per-rank artifact pointers, re-run recipe). Pure functions, no torch import, disabled via ACCELERATE_DISTRIBUTED_ERROR_SUMMARY=0; any internal formatting error degrades to None so a bug in diagnostics can never mask the real crash.
… work for multi-GPU - Catch ChildFailedError in multi_gpu_launcher/deepspeed_launcher (in-process branch), print one aggregated diagnostic, then re-raise unchanged (strictly additive default). With --quiet, exit with the first failure's code mapped to the shell convention (signal -N -> 128+N), extending --quiet to the multi-GPU path its help text currently disclaims. - Expose torchrun's --redirects and --local_ranks_filter passthroughs; refuse the lossy configuration by auto-enabling --tee/--log_dir when filtering is requested without file capture (demote, never delete). - simple_launcher: exit 130 on KeyboardInterrupt instead of dumping the launcher's own traceback (huggingface#1089). - Tests: unit coverage for the classifier/formatter plus an in-process harness that reaches the real except clause on zero-GPU runners by monkeypatching torch.distributed.run.run (a --cpu subprocess test would dispatch to simple_launcher and never reach it). Refs huggingface#1089
4 tasks
mosafariuk
force-pushed
the
launch-failure-diagnostics
branch
from
July 29, 2026 16:31
0f48cce to
aa791b7
Compare
Author
|
@muellerzr @SunMarc @BenjaminBossan — gentle ping on this one. It's been open since Jul 28 with no reviewer assigned, so I suspect it just didn't land in anyone's queue rather than anyone deciding against it. The 5 workflows are also still awaiting maintainer approval, so there's no CI signal yet either — if someone could approve the run, Happy to split it if the scope is the blocker: the |
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What does this PR do?
When a rank dies during
accelerate launch --multi_gpu(or FSDP / single-node DeepSpeed),torch.distributed.elastichands accelerate a fully structuredChildFailedError— oneProcessFailureper rank with exitcode, signal, pid, timestamp and error-file path. Today the launchers catch it with a genericexcept Exceptionand re-raise unchanged, so that structure reaches the user as raw traceback text underneath the per-rank log flood — and torchelastic's own "Root Cause (first observed failure)" heuristic can name a victim rank (one that timed out waiting) while the true culprit is the rank that logged nothing.This PR reads that object and prints one bordered diagnostic block: the first observed failure (rank, pid, exitcode, signal) explicitly labelled as torchelastic's earliest-timestamp heuristic, a signal-class breakdown separating watchdog-style aborts from ranks the elastic agent tore down afterwards ("collateral, not causes"), pointers to every preserved per-rank artifact, and the exact re-run command for per-rank capture and Flight Recorder. It also:
--local_ranks_filter/--redirects(accelerate already forwards--tee/--log_dirthrough the same_filter_argspath) — and refuses the lossy configuration: filtering without file capture auto-enables--tee 3 --log_dirwith a warning, so muted console output is demoted to files, never destroyed;--quietapply to the multi-GPU path (its help text currently disclaims it, which is part of what how to cleanly exit when usingaccelerate launch#1089 reports); in quiet mode the exit code becomes128 + signum(a watchdog SIGABRT yields 134 instead of a lossy 1);KeyboardInterruptinsimple_launcher, so Ctrl-C exits 130 instead of dumping accelerate's own traceback — the direct ask in how to cleanly exit when usingaccelerate launch#1089;accelerate.utils.error_reporting) with notorchimport, unit-testable without GPUs.The three modes, stated precisely: the default interprets the failure (strictly additive — nothing is removed);
--quietsilences the Python-side launcher traceback;--local_ranks_filtermutes the C++-side console flood with preservation (torchrun's OS-level redirection; per-rank files always written). No mode deletes diagnostic data.Verification (real hardware)
ChildFailedErrorunchanged (no logs masked).--quiet: exit code 134 (128+SIGABRT), zero launcher tracebacks, single diagnostic report.--local_ranks_filter: eliminated the C++ console flood (169 -> 81 lines, zero watchdog lines on console) while the guard auto-enabled--tee/--log_dir; the full C++ stack traces were verified preserved in per-rankstderr.logfiles.tests/test_cli.pyandtests/test_utils.pypass.Before — raw waterfall on the L40s (excerpt of 169 lines)
After — the diagnostic block this PR adds (default mode, printed before the unchanged re-raise)
What this deliberately does NOT do
ProcessGroupNCCLis not built for recovery. This PR improves presentation of a crash the launcher already handles.std::terminate(RFC [RFC] Asynchronous Error Handling for Distributed Training with NCCL pytorch/pytorch#46874, by design); this PR classifies the resulting SIGABRT at the launcher, where it is observable.ChildFailedError(same type, same exit semantics).ACCELERATE_DISTRIBUTED_ERROR_SUMMARY=0disables the block entirely. If the formatter itself fails for any reason it returnsNoneand the original exception propagates exactly as before — a bug in the diagnostics can never hide the real crash.Scope limitation: DeepSpeed multi-node
DeepSpeed's PDSH/OpenMPI multi-node launchers run through
subprocess.Popenand expose only the child's return code, so there is noChildFailedErroron that path (unless elastic training is enabled); behavior there is unchanged.Tests
tests/test_error_reporting.py— classifier/formatter units (signal bucketing, minimum-timestamp selection,-6 -> 134mapping, empty-input safety, the heuristic caveat, theNone-fallback guard), plus an in-process harness that reaches the realexcept ChildFailedErrorclause on zero-GPU CI by monkeypatchingtorch.distributed.run.run— chosen deliberately because a--cpusubprocess test dispatches tosimple_launcherand never reaches the clause. A real watchdog firing is not CI-inducible on hosted runners; the hardware validation above covers it.Backwards compatibility
New code executes only on the crash path or when a new flag is passed. Exit codes and exception types are unchanged by default.
Refs #1089. Related reports of this failure mode: #3861, #2183, #314, #223. Precedent for launcher-layer mitigation of a distributed footgun: #2195.
Who can review?
@muellerzr @SunMarc @BenjaminBossan