STEPS TO REPRODUCE 1. Have KWin build from git master (2ce9929c4faab90860e069f57f8aed3a967e57a0) on KDE Linux 2. Put laptop to sleep by closing its lid 3. While in that state, plug in a USB-C monitor 4. Open the lid to wake it up OBSERVED RESULT kwin_wayland crashed: #0 0x00007fa4b9ca690c in ??? () at /usr/lib/libc.so.6 #1 0x00007fa4b9c4c3a0 in raise () at /usr/lib/libc.so.6 #2 0x00007fa4bcf736c5 in KCrash::defaultCrashHandler (sig=11) at /home/nate/kde/src/kcrash/src/kcrash.cpp:605 #3 0x00007fa4b9c4c4d0 in <signal handler called> () at /usr/lib/libc.so.6 #4 KWin::LogicalOutput::scale (this=this@entry=0x0) at /home/nate/kde/src/kwin/src/core/output.cpp:392 #5 0x00007fa4bd2ab2c4 in KWin::LogicalOutput::geometryF (this=this@entry=0x0) at /home/nate/kde/src/kwin/src/core/output.cpp:402 #6 0x00007fa4bd284a73 in KWin::Compositor::assignOutputLayers (this=0x56357f2b3a50, output=0x56357f134800) at /home/nate/kde/src/kwin/src/compositor.cpp:1015 #7 0x00007fa4ba3c834f in ??? () at /usr/lib/libQt6Core.so.6 #8 0x00007fa4bd5e9108 in KWin::DrmOutput::applyQueuedChanges (this=this@entry=0x56357f134800, props=std::shared_ptr<KWin::OutputChangeSet> (use count 2, weak count 0) = {...}) at /home/nate/kde/src/kwin/src/backends/drm/drm_output.cpp:492 #9 0x00007fa4bd5ab744 in KWin::DrmBackend::applyOutputChanges (this=0x56357eed0180, config=...) at /home/nate/kde/src/kwin/src/backends/drm/drm_backend.cpp:417 #10 0x00007fa4bd54bb18 in KWin::Workspace::applyOutputConfiguration (this=0x56357f330db0, config=...) at /home/nate/kde/src/kwin/src/workspace.cpp:526 #11 0x00007fa4bd54bf6e in operator() (__closure=0x56357fbfe4d0) at /home/nate/kde/src/kwin/src/workspace.cpp:239 #12 operator() (__closure=<optimized out>) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:116 #13 QtPrivate::FunctorCallBase::call_internal<void, QtPrivate::FunctorCall<std::integer_sequence<long unsigned int>, QtPrivate::List<>, void, KWin::Workspace::init()::<lambda()> >::call(KWin::Workspace::init()::<lambda()>&, void**)::<lambda()> > (args=<optimized out>, fn=<optimized out>) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:65 #14 QtPrivate::FunctorCall<std::integer_sequence<long unsigned int>, QtPrivate::List<>, void, KWin::Workspace::init()::<lambda()> >::call (f=..., arg=<optimized out>) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:115 #15 QtPrivate::FunctorCallable<KWin::Workspace::init()::<lambda()> >::call<QtPrivate::List<>, void> (f=..., arg=<optimized out>) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:337 #16 QtPrivate::QCallableObject<KWin::Workspace::init()::<lambda()>, QtPrivate::List<>, void>::impl(int, QtPrivate::QSlotObjectBase *, QObject *, void **, bool *) (which=<optimized out>, this_=0x56357fbfe4c0, r=<optimized out>, a=<optimized out>, ret=<optimized out>) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:547 #17 0x00007fa4ba3c834f in ??? () at /usr/lib/libQt6Core.so.6 #18 0x00007fa4bd36294d in KWin::InputRedirection::processSpies<void (KWin::InputEventSpy::*)(KWin::SwitchEvent*), KWin::SwitchEvent*> (this=<optimized out>, method=<optimized out>) at /home/nate/kde/src/kwin/src/input.h:166 #19 operator() (__closure=<optimized out>, state=<optimized out>, time=std::chrono::duration = { <optimized out>us }, device=<optimized out>) at /home/nate/kde/src/kwin/src/input.cpp:3329 #20 operator() (__closure=<optimized out>) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:116 #21 QtPrivate::FunctorCallBase::call_internal<void, QtPrivate::FunctorCall<std::integer_sequence<long unsigned int, 0, 1, 2>, QtPrivate::List<KWin::SwitchState, std::chrono::duration<long int, std::ratio<1, 1000000> >, KWin::InputDevice*>, void, KWin::InputRedirection::addInputDevice(KWin::InputDevice*)::<lambda(KWin::SwitchState, std::chrono::microseconds, KWin::InputDevice*)> >::call(KWin::InputRedirection::addInputDevice(KWin::InputDevice*)::<lambda(KWin::SwitchState, std::chrono::microseconds, KWin::InputDevice*)>&, void**)::<lambda()> > (args=<optimized out>, fn=<optimized out>) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:65 #22 QtPrivate::FunctorCall<std::integer_sequence<long unsigned int, 0, 1, 2>, QtPrivate::List<KWin::SwitchState, std::chrono::duration<long int, std::ratio<1, 1000000> >, KWin::InputDevice*>, void, KWin::InputRedirection::addInputDevice(KWin::InputDevice*)::<lambda(KWin::SwitchState, std::chrono::microseconds, KWin::InputDevice*)> >::call (f=<optimized out>, arg=<optimized out>) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:115 #23 QtPrivate::FunctorCallable<KWin::InputRedirection::addInputDevice(KWin::InputDevice*)::<lambda(KWin::SwitchState, std::chrono::microseconds, KWin::InputDevice*)>, KWin::SwitchState, std::chrono::duration<long int, std::ratio<1, 1000000> >, KWin::InputDevice*>::call<QtPrivate::List<KWin::SwitchState, std::chrono::duration<long, std::ratio<1, 1000000> >, KWin::InputDevice*>, void> (f=<optimized out>, arg=<optimized out>) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:337 #24 QtPrivate::QCallableObject<KWin::InputRedirection::addInputDevice(KWin::InputDevice*)::<lambda(KWin::SwitchState, std::chrono::microseconds, KWin::InputDevice*)>, QtPrivate::List<KWin::SwitchState, std::chrono::duration<long int, std::ratio<1, 1000000> >, KWin::InputDevice*>, void>::impl(int, QtPrivate::QSlotObjectBase *, QObject *, void **, bool *) (which=<optimized out>, this_=<optimized out>, r=<optimized out>, a=<optimized out>, ret=<optimized out>) at /usr/include/qt6/QtCore/qobjectdefs_impl.h:547 #25 0x00007fa4ba3c8418 in ??? () at /usr/lib/libQt6Core.so.6 #26 0x00007fa4bd2a7d7d in QMetaObject::activate<void, KWin::SwitchState, std::chrono::duration<long, std::ratio<1l, 1000000l> >, KWin::InputDevice*> (sender=0x56357fe81ec0, mo=0x7fa4bd906620 <KWin::InputDevice::staticMetaObject>, local_signal_index=22, ret=0x0) at /usr/include/qt6/QtCore/qobjectdefs.h:319 #27 KWin::InputDevice::switchToggle (this=this@entry=0x56357fe81ec0, _t1=<optimized out>, _t2=_t2@entry=std::chrono::duration = { 203253572383us }, _t3=<optimized out>, _t3@entry=0x56357fe81ec0) at /home/nate/kde/build/kwin/src/kwin_autogen/include/moc_inputdevice.cpp:851 #28 0x00007fa4bd60b6fc in KWin::LibInput::Connection::processEvents (this=0x56357f1d5260) at /home/nate/kde/src/kwin/src/backends/libinput/connection.cpp:474 #29 0x00007fa4ba3b5994 in QObject::event(QEvent*) () at /usr/lib/libQt6Core.so.6 #30 0x00007fa4bb52b1c0 in QApplicationPrivate::notify_helper(QObject*, QEvent*) () at /usr/lib/libQt6Widgets.so.6 #31 0x00007fa4ba35b958 in QCoreApplication::notifyInternal2(QObject*, QEvent*) () at /usr/lib/libQt6Core.so.6 #32 0x00007fa4ba35bd30 in QCoreApplicationPrivate::sendPostedEvents(QObject*, int, QThreadData*) () at /usr/lib/libQt6Core.so.6 #33 0x00007fa4ba51ed4d in QEventDispatcherUNIX::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/lib/libQt6Core.so.6 #34 0x00007fa4bb200b73 in QUnixEventDispatcherQPA::processEvents(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/lib/libQt6Gui.so.6 #35 0x00007fa4ba366786 in QEventLoop::exec(QFlags<QEventLoop::ProcessEventsFlag>) () at /usr/lib/libQt6Core.so.6 #36 0x00007fa4ba3603f1 in QCoreApplication::exec() () at /usr/lib/libQt6Core.so.6 #37 0x000056355f4f2e94 in main (argc=<optimized out>, argv=<optimized out>) at /home/nate/kde/src/kwin/src/main_wayland.cpp:634 (gdb) SOFTWARE/OS VERSIONS Operating System: KDE Linux 2025-12-31 KDE Plasma Version: 6.5.80 KDE Frameworks Version: 6.23.0 Qt Version: 6.10.1 Kernel Version: 6.18.2-zen2-1-zen (64-bit) Graphics Platform: Wayland Processors: 16 × AMD Ryzen 7 7840U w/ Radeon™ 780M Graphics Memory: 17 GB of RAM (16.0 GB usable) Graphics Processor: AMD Radeon 780M Graphics
I'm not able to reproduce this on kwin & Plasma built from today's git master
Doing that doesn't cause a crash here either
Hmm, it isn't happening anymore here either. Maybe it got fixed by something. Will re-open if I notice it happening again.
#5 0x00007fa4bd2ab2c4 in KWin::LogicalOutput::geometryF (this=this@entry=0x0) at /home/nate/kde/src/kwin/src/core/output.cpp:402 #6 0x00007fa4bd284a73 in KWin::Compositor::assignOutputLayers (this=0x56357f2b3a50, output=0x56357f134800) at /home/nate/kde/src/kwin/src/compositor.cpp:1015 #7 0x00007fa4ba3c834f in ??? () at /usr/lib/libQt6Core.so.6 #8 0x00007fa4bd5e9108 in KWin::DrmOutput::applyQueuedChanges (this=this@entry=0x56357f134800, props=std::shared_ptr<KWin::OutputChangeSet> (use count 2, weak count 0) = {...}) at /home/nate/kde/src/kwin/src/backends/drm/drm_output.cpp:492 #9 0x00007fa4bd5ab744 in KWin::DrmBackend::applyOutputChanges (this=0x56357eed0180, config=...) at /home/nate/kde/src/kwin/src/backends/drm/drm_backend.cpp:417 #10 0x00007fa4bd54bb18 in KWin::Workspace::applyOutputConfiguration (this=0x56357f330db0, config=...) at /home/nate/kde/src/kwin/src/workspace.cpp:526 The Compositor getting triggered by applyOutputConfiguration() seems super duper wrong. The logical outputs are created later after applying the output configuration. Even if the crash doesn't happen now, we have some architectural issues.
After having a second look at code, I see some issues with the outputLayersChanged signal management.
A possibly relevant merge request was started @ https://invent.kde.org/plasma/kwin/-/merge_requests/8642
Git commit c1ce6ef3f040c047cd29e482237404672ba108f5 by Xaver Hugl. Committed on 14/01/2026 at 14:18. Pushed by zamundaaa into branch 'master'. compositor: only update output layers in response to Workspace::outputsChanged Processing output changes before workspace gets a chance to create and update logical outputs can cause problems, for example if - you disable an output -> logical output gets removed - some unrelated output changes happen - you enable the output again, and different output layers get assigned than before In that case, Compositor::assignOutputLayers would get called while there's no logical output yet, and thus KWin crashes. Outputs layers can only change in situations where workspace will later emit outputsChanged, so the intermediary actions were unnecessary either way. M +0 -4 src/backends/drm/drm_pipeline.cpp M +0 -1 src/backends/drm/drm_virtual_output.cpp M +0 -4 src/compositor.cpp M +0 -2 src/core/backendoutput.h https://invent.kde.org/plasma/kwin/-/commit/c1ce6ef3f040c047cd29e482237404672ba108f5
Git commit 31d3aec7e48a8c7e110678c10f0d936de5b3dd86 by Vlad Zahorodnii, on behalf of Xaver Hugl. Committed on 14/01/2026 at 15:29. Pushed by zamundaaa into branch 'Plasma/6.6'. compositor: only update output layers in response to Workspace::outputsChanged Processing output changes before workspace gets a chance to create and update logical outputs can cause problems, for example if - you disable an output -> logical output gets removed - some unrelated output changes happen - you enable the output again, and different output layers get assigned than before In that case, Compositor::assignOutputLayers would get called while there's no logical output yet, and thus KWin crashes. Outputs layers can only change in situations where workspace will later emit outputsChanged, so the intermediary actions were unnecessary either way. (cherry picked from commit c1ce6ef3f040c047cd29e482237404672ba108f5) M +0 -4 src/backends/drm/drm_pipeline.cpp M +0 -1 src/backends/drm/drm_virtual_output.cpp M +0 -4 src/compositor.cpp M +0 -2 src/core/backendoutput.h https://invent.kde.org/plasma/kwin/-/commit/31d3aec7e48a8c7e110678c10f0d936de5b3dd86
*** Bug 514638 has been marked as a duplicate of this bug. ***