Skip to content

fix: OTel operation and attempt events lose the original exception.type - #812

Open
nandanadileep wants to merge 4 commits into
aws:mainfrom
nandanadileep:oss-harness/issue-804
Open

nandanadileep wants to merge 4 commits into
aws:mainfrom
nandanadileep:oss-harness/issue-804

Conversation

@nandanadileep

@nandanadileep nandanadileep commented Oct 22, 2026 •

Copy link
Copy Markdown

Fixes #804.

Preserve the checkpointed exception.type, exception.message, and stack trace on OpenTelemetry operation and attempt events in both execution and invocation views. Record a plain Exception(message) with explicit saved attributes, avoiding imports and application exception constructors during telemetry recording or replay. This also supports ExceptionGroup and BaseExceptionGroup, whose constructors require more than a message.

Shared regression tests cover built-in and unknown exception types, exception groups, constructor side effects, and replayed metadata. The execution view continues to suppress duplicate replayed operation spans.

Validation on Python 3.13.14:

  • hatch run dev-otel:test -q --tb=short: 796 passed, including integration tests.
  • hatch run dev-otel:typecheck: passed (44 source files).
  • hatch fmt --check from the OTel package: passed.
  • git diff --check: passed.

AI-assisted contribution: OpenCode using Autoloops authored the initial patch; Codex implemented the review follow-up and ran the local checks above. No human review is claimed.

@nandanadileep
nandanadileep temporarily deployed to ai-pr-review-runtime October 22, 2026 22:37 — with GitHub Actions Inactive

@zhongkechen zhongkechen left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I found two cases worth covering in the shared tests: exception groups and custom exception constructors.

try:
return cls(message)
except Exception: # noqa: BLE001 - a class may reject positional args
exc: BaseException = cls.__new__(cls)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

ExceptionGroup breaks this fallback too: its __new__ still needs both the message and the contained exceptions. I reproduced this in both views; PluginExecutor catches the TypeError, and the exported operation/attempt span has no exception event.

Could we add an ExceptionGroup regression case and fall back to a plain exception? We're already overriding exception.type and exception.message, so preserving the recorded type doesn't depend on reconstructing the original class.

resolved: type[BaseException] | None = _resolve_exception_class(type_name)
cls: type[BaseException] = resolved or _synthesize_exception_class(type_name)
try:
return cls(message)

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This calls the application's exception constructor again just to record the event. With a custom exception that increments a counter in __init__, I see another increment for each operation/attempt event in both views. Any logging, metrics, or I/O in that constructor would run again too, including when processing stored error metadata.

I think we can avoid resolving and constructing the original class altogether: pass a plain Exception(message) and keep the explicit type/message/stacktrace attributes below. That would also avoid running application constructors during replay.

This branch was previously deployed

1 inactive (outdated) deployment
ai-pr-review-runtime — be97d837 Deployed Oct 22, 2026 by nandanadileep via notify / Generate notification summary #252
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.

[Bug]: OTel operation and attempt events lose the original exception.type

2 participants