Fixing a Hidden Bug in comtypes: When Exception Handling Masks the Real Problem

A bug in comtypes' struct packing logic was silently converting PackingError into UnboundLocalError, making debugging nearly impossible. This fix restores the original error and adds regression tests to catch it in the future.
What
The calc_packing() function in comtypes attempts multiple struct packing strategies. When all of them fail, it tries to re-raise the last PackingError. However, in Python 3, the exception variable bound in an except clause is deleted when the block exits. The code was trying to access this deleted variable, triggering UnboundLocalError instead of showing the real packing failure. The fix captures the last error in a separate variable that persists beyond the exception block.
Why it matters
Exception masking is a silent killer in debugging. When a real PackingError gets hidden behind UnboundLocalError, developers waste time chasing the wrong problem. Struct packing failures are already tricky to diagnose, so preserving the original error message is essential. This fix ensures developers see what actually went wrong.
Who it's for
This affects anyone using comtypes to work with COM structs that have unusual or problematic packing requirements. Library maintainers also benefit from having regression tests that prevent this class of exception handling bugs from sneaking back in.
When & where
This is a draft PR against the enthought/comtypes repository (PR #942). The fix is small and focused, touching only the error handling path in calc_packing(). It includes three new unit tests that verify the correct exception is raised and that the error message is preserved.
How
The solution is straightforward: instead of relying on the except clause variable, store the exception in a separate local variable before the exception block ends. This variable remains accessible for the final re-raise. The regression tests confirm that pre-fix code fails with UnboundLocalError while post-fix code correctly raises PackingError with the original details intact.
Takeaway
Small exception handling bugs can have outsized impact on debugging experience. Python 3's automatic cleanup of exception variables is a feature, but it requires careful coding patterns. Adding regression tests for error paths, not just happy paths, catches these subtle issues early.
Building an AI agent?
I'm packaging how I ship them into one kit. Early access:
AI Agent Starter Kit →