Skip to content

Fix: import ExecutableError from dbt_common and chain original exception in cursor.py - #689

Open
tbrandharrity wants to merge 1 commit into
aws-samples:mainfrom
tbrandharrity:fix-executable-error-import
Open

Fix: import ExecutableError from dbt_common and chain original exception in cursor.py#689
tbrandharrity wants to merge 1 commit into
aws-samples:mainfrom
tbrandharrity:fix-executable-error-import

Conversation

@tbrandharrity

Copy link
Copy Markdown

Problem

GlueCursor.execute() catches any exception (typically transient Glue errors — network timeouts, throttles, session drops) and attempts to re-raise as dbterrors.ExecutableError. However, dbt.exceptions.ExecutableError does not exist in dbt-core ≥ 1.5 — ExecutableError lives in dbt_common.exceptions (which the sibling connection.py correctly imports).

Current behavior: the except block fires, then the raise itself crashes with:

Runtime Error in model X
  module 'dbt.exceptions' has no attribute 'ExecutableError'

This has two consequences:

  1. The original exception is lost — the user sees a misleading Python attribute error instead of the underlying Glue transient error.
  2. Recoverable transients (Glue AWS SDK "Read timed out", session capacity issues, etc.) become fatal build failures. Retry logic upstream can't do anything because the exception type isn't the expected one.

Reproduced on dbt-glue==1.10.19 with dbt-core==1.10.21 when a Glue interactive session had a transient AWS SDK Read timeout during createTable. Killed a 3-hour build.

Fix

Two-line change to dbt/adapters/glue/gluedbapi/cursor.py:

  1. Import ExecutableError from dbt_common.exceptions (extending the existing DbtDatabaseError import).
  2. raise ExecutableError(str(e)) from e instead of the broken raise dbterrors.ExecutableError — preserves the original exception in the traceback via PEP 3134 exception chaining.

Matches the same fix that was applied to connection.py in #434.

Tests

No existing unit test exercises this code path. The bug only surfaces on real transient Glue failures, which are hard to trigger deterministically in tests. Existing test suite still passes.

Happy to add a test if maintainers can suggest the right mock pattern (mocking self.statement.execute() to raise an arbitrary exception, then asserting ExecutableError is raised with the original in __cause__).

… exception

The Glue cursor's execute() catches broad exceptions and re-raises as
dbterrors.ExecutableError, but dbt.exceptions.ExecutableError does not
exist in dbt-core >= 1.5 -- ExecutableError lives in dbt_common.exceptions.

Current behavior: any transient Glue error (network timeout, throttle,
etc.) triggers the except block, and the raise itself crashes with
`module 'dbt.exceptions' has no attribute 'ExecutableError'`. This
obscures the ORIGINAL error and turns recoverable transients into fatal
build failures.

Fix:
1. Import ExecutableError from dbt_common.exceptions (matching connection.py
   which was fixed the same way).
2. Raise ExecutableError(str(e)) from e so the original exception is
   preserved in the traceback.

Test: no unit test currently exercises this path; the bug only surfaces
on real Glue transient failures which are hard to reproduce
deterministically in tests.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant