MutaFix run: boltons-001

ranker nvidia/NVIDIA-Nemotron-3-Nano-30B-A3B · patcher nvidia/nemotron-3-super-120b-a12b · via Nebius Token Factory

fixedoutcome
33swall time
6model calls
$0.0085API cost
Benchmark ground truth (hidden from the agent)
boltons/tbutils.py:397 · operator NEGCOND · function boltons/tbutils.py::ExceptionInfo.from_exc_info
- if not (type_mod not in ('__main__', '__builtin__', 'exceptions', 'builtins')):   (injected bug)+ if type_mod not in ("__main__", "__builtin__", "exceptions", "builtins"):   (original)
retriever rank 15 of 20 · ranker position not in top 5 · patched the right function no · exact restore no

Trace

reproduce
+3.59s
1 failing test(s) · 1 failed, 524 passed · 3.59s
failing tests and errors
tests/test_tbutils.py::test_contextual
tests/test_tbutils.py::test_contextual
E   AssertionError: assert 'builtins.Exc...c_a exception' == 'Exception: func_a exception'
E     
E     - Exception: func_a exception
E     + builtins.Exception: func_a exception
E     ? +++++++++
trace
+4.0s
re-ran 1 test file(s) with the trace plugin: 1 failing and 1 passing tests, 25 library functions ran in a failing test · 0.29s
tests/test_tbutils.py
retrieve
+4.0s
923 functions indexed, 236 reachable from tests/test_tbutils.py::test_contextual in the static call graph. Kept 20 candidates, ranked by spectrum score (Ochiai), call graph and BM25 break ties.
candidates in retriever order
  1. boltons/tbutils.py::ContextualCallpoint.to_dict call depth 1 · Ochiai 1.00 (failing 1, passing 0)
  2. boltons/tbutils.py::ExceptionInfo.get_formatted call depth 1 · Ochiai 1.00 (failing 1, passing 0)
  3. boltons/tbutils.py::ContextualCallpoint.__init__ call depth 1 · Ochiai 1.00 (failing 1, passing 0)
  4. boltons/tbutils.py::Callpoint.to_dict call depth 1 · Ochiai 1.00 (failing 1, passing 0)
  5. boltons/tbutils.py::Callpoint.from_current call depth 1 · Ochiai 1.00 (failing 1, passing 0)
  6. boltons/tbutils.py::ContextualCallpoint.from_frame call depth 2 · Ochiai 1.00 (failing 1, passing 0)
  7. boltons/tbutils.py::Callpoint.from_frame call depth 2 · Ochiai 1.00 (failing 1, passing 0)
  8. boltons/tbutils.py::_DeferredLine.__repr__ call depth 2 · Ochiai 1.00 (failing 1, passing 0)
  9. boltons/tbutils.py::ContextualCallpoint._populate_local_reprs call depth 3 · Ochiai 1.00 (failing 1, passing 0)
  10. boltons/tbutils.py::ContextualCallpoint._populate_context_lines call depth 3 · Ochiai 1.00 (failing 1, passing 0)
  11. boltons/tbutils.py::ContextualCallpoint.from_tb call depth - · Ochiai 1.00 (failing 1, passing 0)
  12. boltons/tbutils.py::Callpoint.__repr__ call depth - · Ochiai 1.00 (failing 1, passing 0)
  13. boltons/tbutils.py::TracebackInfo.get_formatted call depth 1 · Ochiai 0.71 (failing 1, passing 1)
  14. boltons/tbutils.py::ExceptionInfo.from_current call depth 1 · Ochiai 0.71 (failing 1, passing 1)
  15. boltons/tbutils.py::ExceptionInfo.from_exc_info call depth 2 · Ochiai 0.71 (failing 1, passing 1)
  16. boltons/tbutils.py::Callpoint.tb_frame_str call depth 2 · Ochiai 0.71 (failing 1, passing 1)
  17. boltons/tbutils.py::ExceptionInfo.__init__ call depth 2 · Ochiai 0.71 (failing 1, passing 1)
  18. boltons/tbutils.py::_DeferredLine.__str__ call depth 2 · Ochiai 0.71 (failing 1, passing 1)
  19. boltons/tbutils.py::Callpoint.__init__ call depth 2 · Ochiai 0.71 (failing 1, passing 1)
  20. boltons/tbutils.py::TracebackInfo.__init__ call depth 2 · Ochiai 0.71 (failing 1, passing 1)
rank
+4.0s
spectrum order kept (no model call)
  1. boltons/tbutils.py::ContextualCallpoint.to_dict
  2. boltons/tbutils.py::ExceptionInfo.get_formatted
  3. boltons/tbutils.py::ContextualCallpoint.__init__
  4. boltons/tbutils.py::Callpoint.to_dict
  5. boltons/tbutils.py::Callpoint.from_current
read
+6.68s
turn 1 · NVIDIA-Nemotron-3-Nano-30B-A3B · 2523 in / 154 out · 2.66s · $0.00019

read boltons/tbutils.py::ExceptionInfo.get_formatted

The failing test checks that ctx_ei_str.splitlines()[-1] equals 'Exception: func_a exception', but the actual output starts with 'builtins.Exception: func_a exception'. This suggests that ExceptionInfo.get_formatted is including the full qualified name of the exception type (including the module) instead of just the simple name. The method currently concatenates tb_str and f'{self.exc_type}: {self.exc_msg}', but self.exc_type is likely the full class path (e.g., 'builtins.Exception') rather than just 'Exception'. The fix should ensure only the simple exception name is used in the formatted string.

patch
+9.48s
turn 2 · NVIDIA-Nemotron-3-Nano-30B-A3B · 2861 in / 150 out · 2.5s · $0.00021

The test expects the last line to be exactly 'Exception: func_a exception', but the current code uses self.exc_type directly, which includes the full qualified name (e.g., 'builtins.Exception'). By using self.exc_type.__name__, we extract only the simple exception name ('Exception'), matching the expected output.

--- a/boltons/tbutils.py+++ b/boltons/tbutils.py@@ -435,5 +435,5 @@         # TODO: add SyntaxError formatting         tb_str = self.tb_info.get_formatted()-        return ''.join([tb_str, f'{self.exc_type}: {self.exc_msg}'])+        return ''.join([tb_str, f'{self.exc_type.__name__}: {self.exc_msg}'])      def get_formatted_exception_only(self):

tests: 1 failing (target tests) 1 failed

tests/test_tbutils.py::test_contextual
read
+12.89s
turn 3 · nemotron-3-super-120b-a12b · 3107 in / 554 out · 3.42s · $0.00143

read boltons/tbutils.py::ExceptionInfo.__init__

To understand how exc_type is stored in ExceptionInfo, we need to look at its __init__ method.

model reasoning (excerpt)
We need to examine what self.exc_type actually is. It might be a string already? Let's look at ExceptionInfo class definition. We'll read candidate 14 maybe? Actually we have candidate 14: ExceptionInfo.from_current, but we need ExceptionInfo itself. Not listed yet. But we can infer: ExceptionInfo probably stores exc_type as a type (class) or as a string? The error says "'str' object has no attribute '__name__'", meaning self.exc_type is a string. So we cannot use __name__ on a string. Instead, if it's already a string representing the full name, we need to extract the last part after the last dot. Or perhaps we want just the class name without module. For builtins.Exception, the string is 'builtins.Exception', we want 'Exception'.

Alternatively, maybe exc_type is the actual type object, but then why would it be a string? Could be that in some contexts it's stored as string? Let's check the ExceptionInfo class. We'll read it.

But note: The test also passes in some cases? There were passing tests earlier? The candidate list shows some functions ran in 1/1 failing and 1/1 passing tests. That indicates there is at least one passing test. So the bug might be that sometimes exc_type is a type, sometimes a string? Unlikely.

Better to look at ExceptionInfo definition. Let's read the file around that area. We'll do a read for the file boltons/tbutils.py to see ExceptionInfo. But we can only read specific candidates? The action read can take a candidate number, which refers to a fu
read
+13.94s
turn 4 · nemotron-3-super-120b-a12b · 3278 in / 76 out · 1.05s · $0.00105

read boltons/tbutils.py::ExceptionInfo.from_current

Check ExceptionInfo.from_current to see how exc_type is obtained.

model reasoning (excerpt)
We have seen that ExceptionInfo.__init__ stores exc_type as passed in. We need to know what is passed in from ExceptionInfo.from_current or from_exc_info.
 Let's look at those methods.
read
+16.37s
turn 5 · nemotron-3-super-120b-a12b · 3468 in / 504 out · 2.43s · $0.00149

read boltons/tbutils.py::TracebackInfo.__init__

We need to see the definition of ContextualExceptionInfo to understand if it overrides __init__ or any methods that might affect exc_type.

model reasoning (excerpt)
We need to see what sys.exc_info returns: (type, value, traceback). So exc_type is a type object (like Exception), not a string. Then why did our patch cause AttributeError? Because maybe sometimes exc_type is a string? But the error said: AttributeError: 'str' object has no attribute '__name__'. That indicates that in some case, self.exc_type is a string. However, looking at the test, they raise a plain Exception, so exc_type should be the built-in Exception class. Unless there's something else.

Wait, the error message from the test after our patch was:

E   Exception: func_a exception
E   AttributeError: 'str' object has no attribute '__name__'. Did you mean: '__ne__'?

That suggests that when we tried to access __name__ on self.exc_type, it was a string. But why would self.exc_type be a string? Let's check the ExceptionInfo.__init__ again: it sets self.exc_type = exc_type, where exc_type comes from sys.exc_info()[0], which is a class, not a string.

Unless there's another place where ExceptionInfo is instantiated with a string? Or maybe the test is also using some other code path? But the test uses ContextualExceptionInfo.from_current(), which eventually calls ExceptionInfo.from_current().

Let's look at ContextualExceptionInfo. We haven't seen that yet. It might be a subclass. Let's check if it overrides __init__ or something.

But we don't have it in the candidates list. However, we can read it if needed. But first, let's see the test again: it uses ContextualExceptionI
patch
+32.94s
turn 6 · nemotron-3-super-120b-a12b · 3591 in / 3399 out · 12.95s · $0.00414

To extract the simple name of the exception type, whether exc_type is a class or a string representing the qualified name.

--- a/boltons/tbutils.py+++ b/boltons/tbutils.py@@ -435,5 +435,5 @@         # TODO: add SyntaxError formatting         tb_str = self.tb_info.get_formatted()-        return ''.join([tb_str, f'{self.exc_type}: {self.exc_msg}'])+        return ''.join([tb_str, f'{str(self.exc_type).split(".")[-1]}: {self.exc_msg}'])      def get_formatted_exception_only(self):

tests: all tests pass 525 passed

model reasoning (excerpt)
We have exhausted our reads. Let's think about the problem again.

From the error message:
  E   Exception: func_a exception
  E   AttributeError: 'str' object has no attribute '__name__'

This indicates that in the patched version, we tried to access `__name__` on a string. So `self.exc_type` is a string, not a type.

Looking at the `__init__` of ExceptionInfo (candidate 17), we see:
    self.exc_type = exc_type

And from `from_current` -> `from_exc_info` (which we haven't seen, but we know from the traceback that it uses `sys.exc_info()`).

In standard Python, `sys.exc_info()` returns a tuple: (type, value, traceback). The first element is the exception type (a class), not a string.

However, note that the test is using `ContextualExceptionInfo.from_current()`. We haven't looked at `ContextualExceptionInfo` because it wasn't in the candidates. But note that the test says:

    ctx_ei = ContextualExceptionInfo.from_current()

So we must consider that `ContextualExceptionInfo` might be a subclass of `ExceptionInfo` and might override something.

But wait, we don't have `ContextualExceptionInfo` in the candidates. However, note that the test imports from `boltons.tbutils` and we have been looking at `boltons/tbutils.py`. It's possible that `ContextualExceptionInfo` is defined in the same file.

Given that we have no more reads, we must rely on what we have.

Alternatively, let's look at the `ExceptionInfo.from_exc_info` method (candidate 15) even though we didn't get to read i

Final patch

--- a/boltons/tbutils.py+++ b/boltons/tbutils.py@@ -435,5 +435,5 @@         # TODO: add SyntaxError formatting         tb_str = self.tb_info.get_formatted()-        return ''.join([tb_str, f'{self.exc_type}: {self.exc_msg}'])+        return ''.join([tb_str, f'{str(self.exc_type).split(".")[-1]}: {self.exc_msg}'])      def get_formatted_exception_only(self):