Skip to content

Abort in free-threading validate_refcounts: "tracked objects must have a reference count > 0" #142975

@devdanzin

Description

@devdanzin

Crash report

What happened?

The following code will trigger an abort in a free-threading debug build:

from threading import Thread
import gc

for x in range(50):
    gc.freeze()
    gc.collect()
    gc.unfreeze()

    alive = [Thread(target=gc.unfreeze) for x in range(10)]
    for t in alive: t.start()

Abort message:

Python/gc_free_threading.c:1099: validate_refcounts: Assertion "_Py_REFCNT(((PyObject*)((op)))) > 0" failed: tracked objects must have a reference count > 0
Enable tracemalloc to get the memory block allocation traceback

object address  : 0x7bffb27415d0
object refcount : 0
object type     : 0x5555566e4ea0
object type name: method
object repr     : <refcnt 0 at 0x7bffb27415d0>

Fatal Python error: _PyObject_AssertFailed: _PyObject_AssertFailed
Python runtime state: initialized

Stack (most recent call first):
  File "/home/danzin/crashers/reduced_gc_abort_source.py", line 6 in <module>

Thread 1 "python" received signal SIGABRT, Aborted.

Backtrace:

#0  __pthread_kill_implementation (threadid=<optimized out>, signo=6, no_tid=0) at ./nptl/pthread_kill.c:44
#1  __pthread_kill_internal (threadid=<optimized out>, signo=6) at ./nptl/pthread_kill.c:89
#2  __GI___pthread_kill (threadid=<optimized out>, signo=signo@entry=6) at ./nptl/pthread_kill.c:100
#3  0x00007ffff7c45e2e in __GI_raise (sig=sig@entry=6) at ../sysdeps/posix/raise.c:26
#4  0x00007ffff7c28888 in __GI_abort () at ./stdlib/abort.c:77
#5  0x0000555556037974 in fatal_error_exit (status=status@entry=-1) at Python/pylifecycle.c:3284
#6  0x000055555603763b in fatal_error (fd=fd@entry=2, header=header@entry=1, prefix=prefix@entry=0x5555563c1340 <__func__._PyObject_AssertFailed> "_PyObject_AssertFailed",
    msg=msg@entry=0x5555563c1340 <__func__._PyObject_AssertFailed> "_PyObject_AssertFailed", status=status@entry=-1) at Python/pylifecycle.c:3493
#7  0x0000555556033520 in _Py_FatalErrorFunc (func=0x5555563c1340 <__func__._PyObject_AssertFailed> "_PyObject_AssertFailed",
    msg=0x5555563c1340 <__func__._PyObject_AssertFailed> "_PyObject_AssertFailed") at Python/pylifecycle.c:3516
#8  0x00005555557f143b in _PyObject_AssertFailed (obj=obj@entry=0x7bffb27415d0, expr=<optimized out>, msg=<optimized out>, file=<optimized out>, line=line@entry=1099,
    function=<optimized out>) at Objects/object.c:3166
#9  0x0000555555f90d9e in validate_refcounts (heap=0x555556940870 <_PyRuntime+410288>, area=<optimized out>, block=<optimized out>, block_size=<optimized out>, args=<optimized out>)
    at Python/gc_free_threading.c:1098
#10 0x0000555555c24b52 in _mi_heap_area_visit_blocks (area=0x7bfff5c4bfa0, page=<optimized out>, visitor=<optimized out>, arg=<optimized out>) at Objects/mimalloc/heap.c:619
#11 0x0000555555c2590f in mi_heap_area_visitor (heap=0x555556940870 <_PyRuntime+410288>, xarea=0x7bfff5c4bfa0, arg=<optimized out>) at Objects/mimalloc/heap.c:681
#12 mi_heap_visit_areas_page (heap=0x555556940870 <_PyRuntime+410288>, page=0x7bffb20033c8, pq=<optimized out>, vfun=<optimized out>, arg=<optimized out>) at Objects/mimalloc/heap.c:661
#13 mi_heap_visit_pages (heap=0x555556940870 <_PyRuntime+410288>, fn=<optimized out>, arg1=<optimized out>, arg2=<optimized out>) at Objects/mimalloc/heap.c:46
#14 mi_heap_visit_areas (heap=0x555556940870 <_PyRuntime+410288>, visitor=<optimized out>, arg=<optimized out>) at Objects/mimalloc/heap.c:667
#15 mi_heap_visit_blocks (heap=0x555556940870 <_PyRuntime+410288>, visit_blocks=<optimized out>, visitor=<optimized out>, arg=<optimized out>) at Objects/mimalloc/heap.c:692
#16 0x0000555555f85bd4 in gc_visit_heaps_lock_held (interp=0x555556906580 <_PyRuntime+171968>, visitor=0x555555f90ab0 <validate_refcounts>, arg=0x7fffffffbc30)
    at Python/gc_free_threading.c:414
#17 gc_visit_heaps (interp=interp@entry=0x555556906580 <_PyRuntime+171968>, visitor=0x555555f90ab0 <validate_refcounts>, arg=arg@entry=0x7fffffffbc30) at Python/gc_free_threading.c:453
#18 0x0000555555f88032 in gc_collect_internal (interp=0x555556906580 <_PyRuntime+171968>, generation=2, state=<optimized out>) at Python/gc_free_threading.c:2274
#19 gc_collect_main (tstate=<optimized out>, generation=2, reason=_Py_GC_REASON_MANUAL) at Python/gc_free_threading.c:2438
#20 0x00005555561072ef in gc_collect_impl (module=0x7bffb232a830, generation=2) at Modules/gcmodule.c:93
#21 gc_collect (module=<optimized out>, args=0x7fffffffbe28, nargs=<optimized out>, kwnames=0x0) at Modules/clinic/gcmodule.c.h:143
#22 0x0000555555e821aa in _Py_BuiltinCallFastWithKeywords_StackRefSteal (callable=callable@entry=..., arguments=0x7e8ff6fe5298, total_args=total_args@entry=0) at Python/ceval.c:1122
#23 0x0000555555e9d508 in _PyEval_EvalFrameDefault (tstate=<optimized out>, frame=<optimized out>, throwflag=<optimized out>) at Python/generated_cases.c.h:2336
#24 0x0000555555e807f4 in _PyEval_EvalFrame (tstate=0x55555693ec48 <_PyRuntime+403080>, frame=0x7e8ff6fe5220, throwflag=0) at ./Include/internal/pycore_ceval.h:119
#25 _PyEval_Vector (tstate=tstate@entry=0x55555693ec48 <_PyRuntime+403080>, func=func@entry=0x7bffb2483c70, locals=locals@entry=0x7bffb26fc8b0, args=args@entry=0x0,
    argcount=argcount@entry=0, kwnames=kwnames@entry=0x0) at Python/ceval.c:2482

Seems to be related to #126312, but I think that one should be fixed (and the abort is different).

Found using fusil by @vstinner.

CPython versions tested on:

CPython main branch

Operating systems tested on:

Linux

Output from running 'python -VV' on the command line:

Python 3.15.0a3+ free-threading build (heads/main:610aabfef2f, Dec 19 2025, 07:12:28) [Clang 21.1.2 (2ubuntu6)]

Metadata

Metadata

Assignees

No one assigned

    Labels

    interpreter-core(Objects, Python, Grammar, and Parser dirs)topic-free-threadingtype-crashA hard crash of the interpreter, possibly with a core dump

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions