Repository navigation
Crashes with Julia 1.12 and Revise.jl #1095
Copy link
Copy link
Closed
Description
I have been using PyPlot (with PyCall) with Revise.jl quite well in the past years, but after the upgrade to Julia 1.12 I see now constant crashes. I've tried to debug these, but they happen quite randomly. I was about to point the issue as happening only in IJulia kernels, when I started seeing them in Julia REPL as well.
These crashes can happen whenever, and are not related to calling "plot" functions.
I have tried reinstalling PyCall and PyPlot with ENV["PYTHON"] = "" (today) and verified the installation happens with a clean Conda.jl environment.
Here is an example of simple commands that trigger the crash:
$ julia
_
_ _ _(_)_ | Documentation: https://docs.julialang.org
(_) | (_) (_) |
_ _ _| |_ __ _ | Type "?" for help, "]?" for Pkg help.
| | | | | | |/ _` | |
| | |_| | | | (_| | | Version 1.12.3 (2025-12-15)
_/ |\__'_|_|_|\__'_| | Official https://julialang.org release
|__/ |
julia> using Base.Threads
julia> threadpool()
:interactive
julia> nthreads()
1
julia> Threads.nthreads(:default)
1
julia> using Revise
julia> using PyPlot
[857707] signal 11 (1): Segmentation fault
in expression starting at REPL[6]:1
_PyInterpreterState_GET at /usr/local/src/conda/python-3.12.12/Include/internal/pycore_pystate.h:133 [inlined]
notify_code_watchers at /usr/local/src/conda/python-3.12.12/Objects/codeobject.c:32
code_dealloc at /usr/local/src/conda/python-3.12.12/Objects/codeobject.c:1705
pydecref_ at /home/mazza/.julia/packages/PyCall/1gn3u/src/PyCall.jl:118 [inlined]
pydecref at /home/mazza/.julia/packages/PyCall/1gn3u/src/PyCall.jl:123
jfptr_pydecref_8808 at /home/mazza/.julia/compiled/v1.12/PyCall/GkzkC_o0cEr.so (unknown line)
run_finalizer at /cache/build/tester-amdci4-9/julialang/julia-release-1-dot-12/src/gc-common.c:180
jl_gc_run_finalizers_in_list at /cache/build/tester-amdci4-9/julialang/julia-release-1-dot-12/src/gc-common.c:270
run_finalizers at /cache/build/tester-amdci4-9/julialang/julia-release-1-dot-12/src/gc-common.c:316
ijl_gc_collect at /cache/build/tester-amdci4-9/julialang/julia-release-1-dot-12/src/gc-stock.c:3483
maybe_collect at /cache/build/tester-amdci4-9/julialang/julia-release-1-dot-12/src/gc-stock.c:349 [inlined]
ijl_gc_managed_malloc at /cache/build/tester-amdci4-9/julialang/julia-release-1-dot-12/src/gc-stock.c:3811
jl_alloc_genericmemory_unchecked at /cache/build/tester-amdci4-9/julialang/julia-release-1-dot-12/src/genericmemory.c:38
_new_genericmemory_ at /cache/build/tester-amdci4-9/julialang/julia-release-1-dot-12/src/genericmemory.c:68 [inlined]
jl_alloc_genericmemory at /cache/build/tester-amdci4-9/julialang/julia-release-1-dot-12/src/genericmemory.c:101
ijl_array_grow_end at /cache/build/tester-amdci4-9/julialang/julia-release-1-dot-12/src/array.c:222
_append_symbol_to_bindings_array at /cache/build/tester-amdci4-9/julialang/julia-release-1-dot-12/src/module.c:1958 [inlined]
append_module_names at /cache/build/tester-amdci4-9/julialang/julia-release-1-dot-12/src/module.c:1980
ijl_module_names at /cache/build/tester-amdci4-9/julialang/julia-release-1-dot-12/src/module.c:2001
#unsorted_names#4 at ./runtime_internals.jl:119 [inlined]
unsorted_names at ./runtime_internals.jl:119 [inlined]
#names#3 at ./runtime_internals.jl:118 [inlined]
names at ./runtime_internals.jl:118 [inlined]
_subtypes_in! at /cache/build/tester-amdci4-9/julialang/julia-release-1-dot-12/usr/share/julia/stdlib/v1.12/InteractiveUtils/src/InteractiveUtils.jl:266
subtypes at /cache/build/tester-amdci4-9/julialang/julia-release-1-dot-12/usr/share/julia/stdlib/v1.12/InteractiveUtils/src/InteractiveUtils.jl:303 [inlined]
_foreach_subtype! at /home/mazza/.julia/packages/Revise/icvyF/src/visit.jl:43
_foreach_subtype! at /home/mazza/.julia/packages/Revise/icvyF/src/visit.jl:49
_foreach_subtype! at /home/mazza/.julia/packages/Revise/icvyF/src/visit.jl:49
foreach_subtype at /home/mazza/.julia/packages/Revise/icvyF/src/visit.jl:38 [inlined]
#__init__##2 at /home/mazza/.julia/packages/Revise/icvyF/src/packagedef.jl:1658
unknown function (ip: 0x7f711c6a894f) at (unknown file)
jl_apply at /cache/build/tester-amdci4-9/julialang/julia-release-1-dot-12/src/julia.h:2391 [inlined]
start_task at /cache/build/tester-amdci4-9/julialang/julia-release-1-dot-12/src/task.c:1249
Allocations: 5202189 (Pool: 5202112; Big: 77); GC: 7
Segmentation fault (core dumped)
Metadata
Metadata
Assignees
Labels
No labels
Activity
yuyichao commentedon Jan 7, 2026
I’ve seen these happen without revise when the finalizer runs on a thread that python isnt aware of.
axsk commentedon Jan 13, 2026
Just coming back to Julia after a christmas break, updated to 1.12.4, and was immediately greeted with exactly this as well :(
Just for reference, as this might be related: #1090
Only workaround that works so far for me:
julia --threads=1...axsk commentedon Jan 13, 2026
I also seemingly randombly obtained this variation of this error
Error
fabmazz commentedon Jan 13, 2026
a2ray commentedon Feb 9, 2026
I too thought this was Revise related, but no, it was the reandomness that fooled me into thinking so. Same error as the OP
and starting with 1,0 for threads seems to solve it for now on julia-1.12.4.
This is only happening on the Linux cluster though, not on my Mac M2 -- perhaps threading is handled differently there?
kafisatz commentedon May 12, 2026
I run into julia crashes very often.
Many of my packages use PyCall.
I find it very difficult to tell whether the crash is Revise or PyCall related (or both).
Also, I don't know yet where to retrieve the logs in VS code when Julia crashes (I am sure it is somewhere). Thus I often us a plain terminal (pwsh, wt) trying to reproduce the crash.
But it turns out that the crashes are not alyways reproducible.
I am mainly commenting here to 'subscribe' to this issue.
Below is a log from today. Unfortunately I cannot share the full code (as it is used for client work).
a2ray commentedon May 12, 2026
daniel-sali commentedon May 21, 2026
I also ran into this issue with a Julia notebook using Julia 1.12.6 and Python 3.13.3 on Ubuntu Noble:
Root Cause Analysis
The crash is caused by
pydecref_being called from Julia's GC finalizer system on a thread that does not hold the Python GIL.Trigger mechanism
Julia runs pending finalizers not only during GC collection, but also when unlocking internal mutexes (specifically
jl_typeinf_lockafter type inference). This means any Julia thread performing compilation can triggerpydecref_finalizers — and these threads never acquire the Python GIL.Call chain (from GDB core dump analysis)
The
_finalized[]guard only protects against calls afterPy_Finalize— it does nothing for the multi-threaded case during normal operation.Why "Run All" triggers it but manual cell execution doesn't
When cells are executed rapidly (Run All), Julia's JIT compiler runs type inference on background threads. Those threads accumulate finalizers for
PyObjects created during earlier cells. When the type inference lock is released, the pending finalizers fire — on the compiler thread, without the GIL.Manual step-by-step execution gives Julia time to run finalizers on the main thread (which holds the GIL via PyCall), so the race never occurs.
Why
GC.enable(false)doesn't helpFinalizers triggered by
_jl_mutex_unlockbypass the GC-enable flag — they are not part of the GC collection cycle.Fix
Use
PyGILState_Check()to only callPy_DecRefwhen the current thread already holds the GIL. If a finalizer runs on a thread without the GIL, skip the decref:The same fix should be applied to
pydecref(::PyBuffer)in pybuffer.jl.Why not
PyGILState_Ensure/PyGILState_Release?We initially tried wrapping with
PyGILState_Ensure/Release, but this causes deadlocks: the finalizer thread blocks waiting for the GIL while the main thread (holding the GIL inside a PyCall ccall) waits for that thread's mutex unlock to complete.It also segfaults during process exit because
PyGILState_Ensuretries to allocate a new thread state after the interpreter is partially torn down (even whenPy_IsInitialized()still returns true).Trade-off
The
PyGILState_Checkapproach leaks Python objects when finalizers run on non-GIL-holding threads. In practice this is a small leak — most finalizers run on the main thread during normal operation. The alternative (crash or deadlock) is far worse.A more complete fix would queue deferred decrefs and process them on the main thread, but that requires more invasive changes to PyCall.
Environment