Skip to content

whisper : catch C++ exceptions in whisper_init_with_params_no_state#3831

Merged
danbev merged 1 commit into
ggml-org:masterfrom
danscMax:fix/whisper-init-exception-safety
Jun 2, 2026
Merged

whisper : catch C++ exceptions in whisper_init_with_params_no_state#3831
danbev merged 1 commit into
ggml-org:masterfrom
danscMax:fix/whisper-init-exception-safety

Conversation

@danscMax

Copy link
Copy Markdown
Contributor

Problem

whisper_init_with_params_no_state is extern "C" and already reports failure by returning NULL (the if (!whisper_model_load(...)) branch). But whisper_model_load can throw instead of returning false:

  • std::runtime_error from src/whisper.cpp itself (e.g. "failed to create ggml context", "failed to find a compatible buffer type for tensor ...").
  • vk::SystemError / vk::OutOfDeviceMemoryError from the ggml-vulkan backend during device/buffer allocation (ggml_vk_create_bufferdevice.allocateMemory, and logical-device createDevice).

Because whisper_init_* are extern "C", a C++ exception unwinding across that boundary into a non-C++ caller aborts the process. From Rust (whisper-rs) the failure looks like:

whisper_model_load: n_langs = 100
fatal runtime error: Rust cannot catch foreign exceptions, aborting

and the process exits with STATUS_STACK_BUFFER_OVERRUN (0xC0000409) on Windows. A recoverable backend/init error thus becomes a hard crash the caller cannot handle — even though the function is designed to report failure via NULL.

Observed

Loading a GGML model on a Vulkan backend that transiently fails device init at startup — vk::PhysicalDevice::createDevice: ErrorInitializationFailed on an NVIDIA RTX 5090 during the first Vulkan device bring-up — aborts the host process instead of returning NULL. A load attempt moments later succeeds, so it is a recoverable transient.

Fix

Wrap the whisper_model_load call in try/catch and funnel any throw into the existing NULL-return path. This keeps the documented "returns NULL on failure" contract for every failure mode and guarantees no C++ exception crosses the extern "C" boundary. Callers already handle NULL (e.g. whisper_init_from_file_with_params, and the language bindings), so a backend failure now degrades to a normal init error instead of aborting the host.

whisper_init_from_{file,buffer}_with_params_no_state both build a whisper_model_loader and route through whisper_init_with_params_no_state, so this single guard covers both entry points.

No behavior change on success; only throw → abort becomes throw → NULL.

whisper_model_load() can throw instead of returning false: std::runtime_error
from this file (failed ggml context / no compatible buffer type), or
vk::SystemError / vk::OutOfDeviceMemoryError from the ggml-vulkan backend during
device/buffer allocation.

whisper_init_* are extern "C", so a C++ exception unwinding across that boundary
aborts non-C++ callers (Rust via whisper-rs, Go via cgo) -- on Windows
STATUS_STACK_BUFFER_OVERRUN (0xC0000409) -- even though the function already
returns NULL on failure. Wrap whisper_model_load() in try/catch and route any
throw into the existing NULL-return path.

Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
danbev added a commit to danbev/whisper.cpp that referenced this pull request Jun 2, 2026
This commit adds exception handling to the parakeet_model_load function
call.

The motivation for this is to avoid exceptions being thrown from this
function as it is part of the extern C interface and instead log the
error and return nullptr.

Refs: ggml-org#3831
@danbev danbev merged commit 610e664 into ggml-org:master Jun 2, 2026
57 of 61 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants