Skip to content

fix: register exception translator as module-local - #315

Merged
merlimat merged 1 commit into
apache:mainfrom
StevenReitsma:fix/local-exception-translator
Oct 22, 2026
Merged

merlimat merged 1 commit into
apache:mainfrom
StevenReitsma:fix/local-exception-translator

Conversation

@StevenReitsma

@StevenReitsma StevenReitsma commented Oct 20, 2026 •

Copy link
Copy Markdown
Contributor

Note: AI was used to generate this description text.

Fixes #314

Motivation

export_exceptions registers a global pybind11 exception translator that ends with a catch (const std::exception&) catch-all. pybind11 tries global translators newest-first, so when pulsar is imported after another pybind11 extension (e.g. duckdb), this translator handles that module's C++ exceptions and turns them into a plain RuntimeError. The other module's own translator never runs, so code catching e.g. duckdb.CatalogException breaks, depending on import order.

Modifications

Register the translator with py::register_local_exception_translator (available since pybind11 2.8; this repo uses 3.0.1). Local translators only apply to exceptions thrown from functions bound in _pulsar, and are tried before any global translators, so:

  • Pulsar's own mappings are unchanged (PulsarException subclasses, std::invalid_argument → ValueError, other std::exception → RuntimeError).
  • Exceptions from other pybind11 modules are no longer intercepted.

Verifying this change

I did not build _pulsar itself locally. To check the pybind11 semantics, I built two minimal modules against pybind11 v3.0.1: one mimicking duckdb (a global translator mapping its own C++ exception to a custom Python exception), and one with the same translator body as src/exceptions.cc. I imported the duckdb-like module first:

Translator registration Other module's exception PulsarException std::invalid_argument other std::exception
register_exception_translator (before) RuntimeError ❌ PulsarException ValueError RuntimeError
register_local_exception_translator (after) CatalogException ✅ PulsarException ValueError RuntimeError

The existing test suite covers the Pulsar-side exception mapping.

The global exception translator ended with a catch-all for std::exception,
so it intercepted C++ exceptions thrown by other pybind11 modules imported
before pulsar (e.g. duckdb) and turned them into RuntimeError, bypassing
those modules' own translators.

Registering it with register_local_exception_translator limits it to
exceptions thrown from _pulsar, leaving other modules' translation intact.

Fixes apache#314
@StevenReitsma
StevenReitsma force-pushed the fix/local-exception-translator branch from 726b405 to be947ae Compare October 20, 2026 17:40
@merlimat
merlimat merged commit bd46d51 into apache:main Oct 22, 2026
11 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.

Global exception translator turns other pybind11 modules' C++ exceptions into RuntimeError (breaks DuckDB error types)

2 participants