fix: update AirbyteTracedException.__str__ to show user-facing message (AI-Triage PR)#927
fix: update AirbyteTracedException.__str__ to show user-facing message (AI-Triage PR)#927devin-ai-integration[bot] wants to merge 5 commits intomainfrom
Conversation
Move the __str__ override from the MessageRepresentationAirbyteTracedErrors subclass into the base AirbyteTracedException class so that str(exception) returns the user-facing message globally, falling back to internal_message. Remove the now-unnecessary MessageRepresentationAirbyteTracedErrors subclass and update the two raise sites in HttpClient to use AirbyteTracedException directly. Co-Authored-By: bot_apk <apk@cognition.ai>
🤖 Devin AI EngineerI'll be helping with this pull request! Here's what you should know: ✅ I will automatically:
Note: I can only respond to comments from users who have write access to this repository. ⚙️ Control Options:
|
👋 Greetings, Airbyte Team Member!Here are some helpful tips and reminders for your convenience. 💡 Show Tips and TricksTesting This CDK VersionYou can test this version of the CDK using the following: # Run the CLI from this branch:
uvx 'git+https://github.com/airbytehq/airbyte-python-cdk.git@devin/1772482932-fix-traced-exception-str#egg=airbyte-python-cdk[dev]' --help
# Update a connector to use the CDK from this branch ref:
cd airbyte-integrations/connectors/source-example
poe use-cdk-branch devin/1772482932-fix-traced-exception-strPR Slash CommandsAirbyte Maintainers can execute the following slash commands on your PR:
|
PyTest Results (Full)3 893 tests 3 881 ✅ 11m 26s ⏱️ Results for commit cdea7d6. ♻️ This comment has been updated with latest results. |
|
/ai-prove-fix |
Add 12 tests in TestAirbyteTracedExceptionStr covering: - str() returns user-facing message when both message and internal_message set - str() falls back to internal_message when message is None - str() returns empty string when both are None - str() works in f-strings and %-formatting - args[0] still contains internal_message for backward compat - Subclasses inherit the __str__ override - from_exception() factory method works correctly - Stack trace and internal_message preserved in trace error Co-Authored-By: bot_apk <apk@cognition.ai>
|
Added 12 tests in
All 22 tests pass locally (10 existing + 12 new). |
There was a problem hiding this comment.
Pull request overview
Promotes the user-facing string representation of AirbyteTracedException into the base class so str(exception) returns message (with fallback), and removes the temporary http_client.py workaround class.
Changes:
- Add
AirbyteTracedException.__str__to prefermessageoverinternal_message. - Remove
MessageRepresentationAirbyteTracedErrorsand raiseAirbyteTracedExceptiondirectly inHttpClient. - Add unit tests validating
__str__behavior across common construction paths and stack trace formatting.
Reviewed changes
Copilot reviewed 3 out of 3 changed files in this pull request and generated 2 comments.
| File | Description |
|---|---|
airbyte_cdk/utils/traced_exception.py |
Adds global __str__ override to return user-facing message (with fallback). |
airbyte_cdk/sources/streams/http/http_client.py |
Removes workaround subclass and updates two raise sites to use AirbyteTracedException. |
unit_tests/utils/test_traced_exception.py |
Adds a new test suite covering the new __str__ semantics and related behaviors. |
💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.
| ) | ||
| airbyte_message = exc.as_airbyte_message() | ||
| assert airbyte_message.trace.error.internal_message == "raw API error: 401" | ||
| assert airbyte_message.trace.error.message == "Authentication failed." |
There was a problem hiding this comment.
Tests cover message=None but don’t cover the case where message is explicitly set to an empty string. Since the codebase uses message="" in at least one place, add a test asserting str(AirbyteTracedException(message="", internal_message="...") ) returns "" (and does not fall back to internal_message) to prevent regressions once __str__ is updated to distinguish empty string from None.
| assert airbyte_message.trace.error.message == "Authentication failed." | |
| assert airbyte_message.trace.error.message == "Authentication failed." | |
| def test_str_with_empty_message_does_not_fall_back_to_internal_message(self) -> None: | |
| """Explicit empty message should be respected and not replaced by internal_message.""" | |
| exc = AirbyteTracedException( | |
| internal_message="an internal error that should not be shown to the user", | |
| message="", | |
| ) | |
| assert str(exc) == "" |
There was a problem hiding this comment.
Added this test in 2dff2e5. Also switched the __str__ implementation to use is not None checks so message="" is properly respected.
| def __str__(self) -> str: | ||
| """Return the user-facing message, falling back to internal_message.""" | ||
|
|
||
| if self.message: | ||
| return self.message | ||
| elif self.internal_message: | ||
| return self.internal_message | ||
| return "" |
There was a problem hiding this comment.
__str__ currently checks if self.message: / elif self.internal_message:, which treats empty strings as “unset”. There is at least one existing call site that sets message="" intentionally; with this implementation str(exc) would fall back to the (potentially very large) internal_message instead of honoring the user-facing message. Consider checking explicitly for is not None (and similarly for internal_message) so message="" results in an empty string rather than leaking internal details.
There was a problem hiding this comment.
Good catch — updated to use is not None checks and added the empty string test. See 2dff2e5.
Address Copilot review: use explicit 'is not None' checks instead of truthiness so that message='' is respected and does not fall back to internal_message. Add test for this edge case. Co-Authored-By: bot_apk <apk@cognition.ai>
…acedException When wrapping an AirbyteTracedException, from_exception now: - Takes internal_message directly from the original instead of via str(exc) - Preserves the original's user-facing message if the caller doesn't provide one This prevents the user-facing message from being lost or incorrectly assigned to internal_message after the __str__ change. Co-Authored-By: sophie.cui@airbyte.io <sophie.cui@airbyte.io>
Co-Authored-By: sophie.cui@airbyte.io <sophie.cui@airbyte.io>
fix: update AirbyteTracedException.str to show user-facing message
Summary
Moves the
__str__override from the temporaryMessageRepresentationAirbyteTracedErrorssubclass into the baseAirbyteTracedExceptionclass, so thatstr(exception)returns the user-facingmessageglobally (falling back tointernal_messageifmessageis not set).The subclass
MessageRepresentationAirbyteTracedErrorsinhttp_client.pywas an acknowledged workaround — its own docstring stated this behavior should eventually be promoted to the base class. This PR completes that migration.Additionally,
from_exceptionis updated to correctly preserve bothinternal_messageandmessagewhen wrapping an existingAirbyteTracedException, rather than going throughstr(exc)which would now return the user-facing message instead of the internal details.Changes:
airbyte_cdk/utils/traced_exception.py: Add__str__that returnsself.message→self.internal_message→""(usingis not Nonechecks to correctly handlemessage="")airbyte_cdk/utils/traced_exception.py: Updatefrom_exceptionto detectAirbyteTracedExceptioninstances and preserve bothinternal_messageandmessagedirectly, instead of usingstr(exc)which would now return the user-facing messageairbyte_cdk/sources/streams/http/http_client.py: RemoveMessageRepresentationAirbyteTracedErrorsclass; update two raise sites to useAirbyteTracedExceptiondirectlyunit_tests/utils/test_traced_exception.py: Add 13 tests inTestAirbyteTracedExceptionStr+ 8 tests inTestFromExceptionPreservesFieldsResolves https://github.com/airbytehq/airbyte-internal-issues/issues/15916
Updates since last revision
from_exceptionnow preserves fields when wrappingAirbyteTracedException: Previouslyfrom_exceptionalways setinternal_message=str(exc). With the new__str__, this would put the user-facing message intointernal_message, losing the actual debug info. Now when wrapping anAirbyteTracedException:internal_messageis taken directly fromexc.internal_message(not viastr())messageis preserved from the original unless the caller explicitly provides oneAirbyteTracedExceptionexceptions, behavior is unchanged (str(exc))TestFromExceptionPreservesFieldscovering: both fields set, only message, only internal_message, neither field, caller override, and regular exception wrappingif self.message:toif self.message is not None:(and similarly forinternal_message) so thatmessage=""is respected. Added test for this edge case.Review & Testing Checklist for Human
from_exceptionfield preservation logic: The newisinstancecheck uses"message" not in kwargsto decide whether to carry over the original'smessage. Verify that all existing call sites passmessageas a keyword argument (not positional via*args). Grep for.from_exception(across the codebase to confirm.__str__change: This changes behavior for everyAirbyteTracedExceptionsubclass across the CDK. Search forstr(e),f"{e}", and logging calls that stringify caughtAirbyteTracedExceptioninstances to verify no critical debug info is lost in logs or error tracking.TracebackException.format()calls__str__, so stack trace one-liners now showmessageinstead ofinternal_message. Verify this doesn't disrupt Sentry grouping or log aggregation that keys on exception string representations.args[0]vs__str__mismatch: The constructor still passesinternal_messagetosuper().__init__(), soexception.args[0]remainsinternal_messagewhilestr(exception)now returnsmessage. Confirm no code relies onargs[0] == str(exception).messagebehavior change: Withis not Nonechecks,message=""now returns""instead of falling back tointernal_message. Verify that call sites settingmessage=""(e.g.,job_orchestrator.py:484) intend forstr(exc)to be empty rather than showing the internal message.Suggested test plan: Run the full CDK test suite (
poetry run poe pytest-fast). Additionally, grep the codebase forstr(e),f"{e}", and%spatterns onAirbyteTracedExceptioninstances to manually verify the behavioral change is safe. Check Sentry dashboards after deployment to confirm error grouping is not disrupted.Notes