Skip to content

FrameLocalsProxy |= and update() mishandle errors raised while merging mappings #153418

Description

@lpyu001

Bug report

Bug description:

Bug description

FrameLocalsProxy does not correctly propagate exceptions raised while merging a dict subclass or another supported mapping-like object.

There are two related problems in Objects/frameobject.c:

  1. framelocalsproxy_inplace_or() returns Py_NotImplemented when framelocalsproxy_merge() fails. If the merge failed because Python code raised an exception, the slot returns a normal object while an exception is still set. In a pydebug build this triggers a fatal _Py_CheckSlotResult error.
  2. framelocalsproxy_update() replaces any failure from framelocalsproxy_merge() with a generic TypeError, masking the original exception.

This is a sub-issue of #146102 with gist details
A minimal reproducer is below.

import operator
import sys

class BadDict(dict):
    def keys(self):
        raise RuntimeError("keys() failed!")

def repro_update():
    locs = sys._getframe().f_locals
    print(type(locs))

    try:
        locs.update(BadDict())
    except TypeError as exc:
        print("BUG: update() masked RuntimeError as TypeError:", exc)
    except RuntimeError as exc:
        print("OK: update() propagated RuntimeError:", exc)

def repro_inplace_or():
    locs = sys._getframe().f_locals
    print(type(locs))

    try:
        operator.ior(locs, BadDict())
    except RuntimeError as exc:
        print("OK: |= propagated RuntimeError:", exc)
    except BaseException as exc:
        print("BUG: |= raised wrong exception:", type(exc).__name__, exc)
    else:
        print("BUG: |= swallowed RuntimeError")

repro_update()
repro_inplace_or()

operator.ior(locs, BadDict()) is used to exercise the same nb_inplace_or slot as locs |= BadDict().

Actual behavior

For .update(), the original RuntimeError from BadDict.keys() is replaced with a generic TypeError:

<class 'FrameLocalsProxy'>
BUG: update() masked RuntimeError as TypeError: update() argument must be dict or another FrameLocalsProxy

For |= on a pydebug build, the interpreter aborts because the slot reports success while an exception remains set:

<class 'FrameLocalsProxy'>
Fatal Python error: _Py_CheckSlotResult: Slot |= of type FrameLocalsProxy succeeded with an exception set
Python runtime state: initialized
Traceback (most recent call last):
  File "/tmp/repro_ior.py", line 6, in keys
    raise RuntimeError("keys() failed!")
RuntimeError: keys() failed!
Aborted (core dumped)

Expected behavior

Both FrameLocalsProxy.update() and FrameLocalsProxy.__ior__() should propagate the original exception raised while merging:

<class 'FrameLocalsProxy'>
OK: update() propagated RuntimeError: keys() failed!
<class 'FrameLocalsProxy'>
OK: |= propagated RuntimeError: keys() failed!

I will submit a PR to fix this

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Linked PRs

Activity

  1. added
    type-bugAn unexpected behavior, bug, or error
    on Jul 9, 2026
  2. added a commit that references this issue on Jul 11, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    interpreter-core(Objects, Python, Grammar, and Parser dirs)type-bugAn unexpected behavior, bug, or error

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions