Crash when undoing a custom operator
If you want the Undo / Redo system to work with a custom operator, you need to add bl_options = {'UNDO'} when defining the class.
Your script then becomes :
import bpy

class CreateEmptyOperator(bpy.types.Operator):
    bl_label = &quot;Create Empty&quot;
    bl_idname = &quot;wm.create_empty&quot;
    bl_options = {'UNDO'}

    def execute(self, context):
        parent = bpy.data.objects.new(&quot;Empty&quot;, None)
        context.collection.objects.link(parent)  # Use the current context when available instead of bpy.context
        return {'FINISHED'}

class MyPanel(bpy.types.Panel):
    bl_idname = &quot;OBJECT_PT_my_panel&quot;
    bl_label = &quot;My Panel&quot;
    bl_space_type = &quot;VIEW_3D&quot;
    bl_region_type = &quot;UI&quot;
    
    def draw(self, context):
        self.layout.operator(&quot;wm.create_empty&quot;)

bpy.utils.register_class(CreateEmptyOperator)
bpy.utils.register_class(MyPanel)