#4526·mlx

ParallelFileReader: uncatchable fatal error when pread lands at EOF

Author: pseudobaconCreated Sep 17, 2026Updated Sep 17, 2026

Summary

Loading .safetensors files through the parallel file reader can abort the process with an uncatchable fatal error when a pread lands at/after EOF. The std::runtime_error thrown by ParallelFileReader crosses the C API boundary and becomes a hard crash instead of a catchable error.

Reproduction

Any multi-reader safetensors load where a reader's pread returns 0 (read at/after EOF):

cpp
// mlx/io/load.cpp  (ParallelFileReader)
// load.cpp:345  static ThreadPool* thread_pool = new ThreadPool{4};  // shared singleton
// load.cpp:378  throw std::runtime_error("[read] Unable to read from file.");  // sync tail read
// load.cpp:392  throw std::runtime_error("[read] Unable to read from file.");  // batched future

When this exception is thrown from a worker thread and propagated across the C API, it is not caught and the process aborts (observed as a Swift fatal error on the consumer side).

Behavior

  • Non-deterministic: the crash fires only when a reader's pread returns 0 at the EOF boundary (a race with the shared ThreadPool{4} work distribution). Re-running the same load usually succeeds.
  • The crash is a process abort, not a catchable exception — no Swift/C caller can recover.

Affected versions

  • C++ MLX ce45c52 (v0.31.x) through 1f8e74e (v0.32.2) — the static ThreadPool* shared reader and the throw std::runtime_error on pread == 0 are present in both.

Smallest failing test

MixedPrecisionQuantLoadTests.perModuleOverridesApplied and MixedPrecisionQuantLoadTests.globalOnlyQuantizationFailsLoudly (and RerankerTests) trigger the multi-reader safetensors load and intermittently abort the test process at the C API boundary (mlx_c/transforms.cpp:73 / array.cpp:352).

Suggested fix

Catch/convert the std::runtime_error from ParallelFileReader before it crosses the C API (return an mlStatus/mlxError instead of throwing), and/or guard the EOF pread == 0 case so it does not throw on the tail read.