Skip to content

M5 Max (T6050) total-system watchdog panic under ~112 GB engine pool: 'no checkins from watchdogd in 91 s' (macOS 27.2/26B123) #735

Open

Description

@brainnoms

Machine / stack

  • Mac Studio M5 Max 128 GB (T6050), macOS 27.2 (26B123), Darwin 27.2.0 xnu-13432.41.4~25 RELEASE_ARM64_T6050
  • iogpu.wired_limit_mb = 124518
  • Splash 1.4.0 serving Qwen3.8-Flash-Next PLE8 — ~112 GB live Metal engine pool — plus 2 MLX qwen3_tts voice sidecars
  • Always-on inference server, sleep disabled; the box runs at zero memory headroom by design

What happened

Two kernel panics in one day (2026-11-09, 14:27 and 18:33 local), identical signature:

panic(cpu N caller ...): watchdog timeout: no checkins from watchdogd in 91 seconds

Total-system stall — every online core reports recently retired instr at 0x0; the ARM watchdog timer is the only thing left breathing. The offline cluster is complementary between the two deaths (first death: MACC0 offline / MACC1 panicked; second death: MACC1 offline / MACC0 panicked). At death, in both panics: memoryPressure: false, compressor effectively empty (2% / 0% of limit), wired ≈ 123.4 GB of 128 GB. Full panic-full bodies captured verbatim and filed with Apple (Feedback Assistant, FB25509461, filed 2026-11-09).

Shadow signatures we see fleet-side (503/500 from Splash across the fall window)

  1. engine_recovering ... native loop did not answer status within 30 s
  2. failed 3 times in a row ... native protocol reached EOF

Splash's engine is the dominant wired resident (~112 GB), so it takes the blame in the shadows — but the kernel panic is a whole-system freeze. On recovery the engine crash-loops through the two signatures above before serving again.

Framing

Metal/ANE residency maintenance under very large wired workloads appears to unwire en masse; on a zero-headroom system that dip is fatal — the stall exceeds the watchdogd checkin interval (>90 s) and the machine panics. Companion 1 s vm_stat telemetry shows the wired pool dipping sharply seconds before the stall.

Related

Standing observation law

The fleet pages on watchdog on every boot — if this falls again we'll append a fresh full panic body to this issue within minutes.

Activity

  1. linson007 commented on Nov 10, 2026

    @linson007
    Contributor

    Thanks for the detailed report and the panic bodies. I checked the current code (1.6.0) against it. Notes and requests below.

    Version. 1.6.0 (Nov 6) is the latest release. This report is on 1.4.0, and 1.6.0 changed the memory governor, the residency set and the restart policy. If you can safely run 1.6.0 on that Mac, that is the most useful next test. The notes below apply either way.

    How the pool is sized

    • The automatic budget is recommendedMaxWorkingSetSize minus the larger of 1 GiB or 2% (runtime/engine/MemoryPlan.hpp). The automatic plan takes the whole budget, and --max-memory can only lower it. The budget caps the engine's Metal allocations, so the sidecars' memory comes on top (docs/server.md, "Memory and context").
    • Splash does not read iogpu.wired_limit_mb. Its budget starts from recommendedMaxWorkingSetSize.
    • On a 128 GB Mac, elective growth keeps a 1 GiB margin above a 7 GiB spare reserve. A request in service can grow past both, short of critical pressure, once nothing else can cover it (docs/internals.md, "host's rule"). At normal pressure, as in both panics, that margin does not hold in-service growth back.
    • --idle-release (default 10m) unwires the engine's memory and frees the weights after an idle period. The next request restores them. An engine exit or restart also releases its memory.

    What would help

    1. The launch command: --max-memory, --idle-release, --expert-memory, and how the two sidecars run.
    2. The memory_plan budget and memory_governor blocks from GET /status, captured while the engine holds its ~112 GB.
    3. The serve log for the minutes before 14:27 and 18:33 on Nov 9. The wired dip came seconds before the stall, so look for an idle release, a /v1/memory/release call, an engine exit or restart, or requests in flight at that time.
    4. The 1 s vm_stat telemetry, if you can share it.
    5. Whether the raised iogpu.wired_limit_mb changes the recommendedMaxWorkingSetSize that /status reports.

    The "failed 3 times in a row" line is the old limit. 1.5.0 stopped at the third failure, and current code stops at six, with backoff between tries (server/supervision.py).

    Related: #220 (sparse GPU mapping stalls and WindowServer logouts on M5 Max 128 GB, closed after 1.2.0 removed placement-sparse KV mapping) and #325 (cache growth on a 128 GB Mac, closed after 1.3.2). Both are useful context, but neither is this mechanism.

    Interim: a --max-memory below the automatic budget leaves room for the sidecars and macOS, as docs/server.md suggests for shared Macs.

    Question for maintainers: should the automatic budget keep more than 2% free on 128 GB Macs when other workloads run beside it? #325 raised the same question for cache growth.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions