Repository: scikit-hep/awkward

Issue:
Error message handling should only show the primary `ErrorContext`
### Version of Awkward Array

HEAD

### Description and code to reproduce

Filling out the previous bug report, the full error message was

```
Traceback (most recent call last):
  File "/home/jpivarski/irishep/awkward/src/awkward/operations/ak_with_field.py", line 47, in with_field
    return _impl(array, what, where, highlevel, behavior)
  File "/home/jpivarski/irishep/awkward/src/awkward/operations/ak_with_field.py", line 87, in _impl
    raise ValueError("no tuples or records in array; cannot add a new field")
ValueError: no tuples or records in array; cannot add a new field

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "/home/jpivarski/irishep/awkward/src/awkward/highlevel.py", line 1025, in __setitem__
    self._layout = ak.operations.with_field(
  File "/home/jpivarski/irishep/awkward/src/awkward/operations/ak_with_field.py", line 47, in with_field
    return _impl(array, what, where, highlevel, behavior)
  File "/home/jpivarski/irishep/awkward/src/awkward/_errors.py", line 54, in __exit__
    self.handle_exception(exception_type, exception_value)
  File "/home/jpivarski/irishep/awkward/src/awkward/_errors.py", line 64, in handle_exception
    raise self.decorate_exception(cls, exception)
ValueError: no tuples or records in array; cannot add a new field

This error occurred while calling

    ak.with_field(
        array = <Array [[{}, {}], [], [{}]] type='3 * var * {}'>
        what = 123
        where = 'new_field'
        highlevel = False
        behavior = None
    )

The above exception was the direct cause of the following exception:

Traceback (most recent call last):
  File "<stdin>", line 1, in <module>
  File "/home/jpivarski/irishep/awkward/src/awkward/highlevel.py", line 1028, in __setitem__
    self._numbaview = None
  File "/home/jpivarski/irishep/awkward/src/awkward/_errors.py", line 54, in __exit__
    self.handle_exception(exception_type, exception_value)
  File "/home/jpivarski/irishep/awkward/src/awkward/_errors.py", line 64, in handle_exception
    raise self.decorate_exception(cls, exception)
ValueError: no tuples or records in array; cannot add a new field

This error occurred while calling

    ak.with_field(
        array = <Array [[{}, {}], [], [{}]] type='3 * var * {}'>
        what = 123
        where = 'new_field'
        highlevel = False
        behavior = None
    )

This error occurred while calling

    ak.Array.__setitem__(
        self = <Array [[{}, {}], [], [{}]] type='3 * var * {}'>
        field_name = 'new_field'
        field_value = 123
    )
```

The `ak.Array.__setitem__` call is calling `ak.with_field`, but only the outermost Awkward call should be printing its context. We generally try to avoid having Awkward high-level functions call other high-level functions (biggest exception: `ak.to_layout`), but when we do, only the first entry into an Awkward high-level function from the outside (`ErrorContext.primary()`) should count.

Considering that Python also prints a chained exception, the notes upon notes dominate the stack trace text, making it hard to see which information is not a duplicate/triplicate/quadruplicate.

(This was introduced by #2370.)


Discussion:
Wait, this is daft! We should definitely be fixing this, I'll take a look. The fix should be fairly simple.

Resolving patch:
diff --git a/src/awkward/_errors.py b/src/awkward/_errors.py
--- a/src/awkward/_errors.py
+++ b/src/awkward/_errors.py
@@ -50,7 +50,7 @@ def __enter__(self):
     def __exit__(self, exception_type, exception_value, traceback):
         try:
             # Handle caught exception
-            if exception_type is not None:
+            if exception_type is not None and self.primary() is self:
                 self.handle_exception(exception_type, exception_value)
         finally:
             # Step out of the way so that another ErrorContext can become primary.
@@ -59,7 +59,7 @@ def __exit__(self, exception_type, exception_value, traceback):
 
     def handle_exception(self, cls: type[E], exception: E) -> E:
         if sys.version_info >= (3, 11, 0, "final"):
-            return self.decorate_exception(cls, exception)
+            self.decorate_exception(cls, exception)
         else:
             raise self.decorate_exception(cls, exception)
 


Test patch: