Bug Splats and Medeek Wall

I had a user today experience a large number of bug splats while using a Space Mouse Pro and adding or editing walls with the Medeek Wall extension. This is the first real case of serious bug splats that I have received with regards to using a space mouse and my extensions.

On a related note I have noticed that sometimes on my older machine (Windows 7 running SU 2017 thru SU 2020) if I click on the Submit button when editing a wall and then quickly rotate or pan the model with my mouse while the edit function is busy rebuilding/redrawing the wall, this can and has caused crashes.

The solution has been to let the model update first, once I edit a wall (usually a second or two) I pause briefly, before I make any sudden or drastic maneuvers of the model with my mouse. This way the model has time to update the geometry fully. For some reason it does not like being panned or rotated too quickly while the geometry is being generated and then rendered back to the screen.

I don’t get any errors in the Ruby Console, it is simply a crash (bug splat), so I never really know what caused it. Perhaps someone a few pay grades above mine could provide me with further guidance in this matter. Is this simply an inherent limitation of SketchUp that I am encountering or is there possibly something I doing wrong in my code which makes SketchUp unstable as it regens some rather complex geometry (ie. a Medeek Wall).

Each wall assembly is typically quite complex, with studs, windows, doors and all the casing and trimming of each that gets fully redrawn when an assembly is regenerated or edited. Since I made everything parametric this is the only way for the geometry to remain fully parametric, it must be all redrawn.

Almost all of plugins use a method similar to the one given below (with a few minor variations) in order to create new or edit existing assemblies. This is from the floor plugin:


def create_floor_assembly

	if @Edit_menu == 1

		if @maingroup.deleted?

			@tempmaingroup = Sketchup.active_model.entities.add_group(@all_subgroups)
			erasestatus = @tempmaingroup.erase!

			UI.messagebox("Floor Assembly has been deleted, action aborted.") 
			raise
		else
			@tempmaingroup = Sketchup.active_model.entities.add_group(@all_trusses)

			# Translates to original origin

			
			orgx = @initgrouporigin[0] * -1
			orgy = @initgrouporigin[1] * -1
			orgz = @initgrouporigin[2] * -1

			main_transformation2 = Geom::Transformation.new([orgx, orgy, orgz])
 			@tempmaingroup.transform! main_transformation2
		
			temptrans = @tempmaingroup.transformation
			tempdef = @tempmaingroup.definition

			group_list = @maingroup.entities.grep(Sketchup::Group)
			comp_list = @maingroup.entities.grep(Sketchup::ComponentInstance)
			grouperase_list = group_list.find_all { |e| e.name !~ /CUSTOM/i && e.name !~ /ELECT_ASSEMBLY/ && e.name !~ /SUBTRACT/i && e.name !~ /FLOORING/ && e.name !~ /OPENING/ }
			comperase_list = comp_list.find_all { |e| e.name !~ /CUSTOM/i && e.name !~ /SUBTRACT/i }

			erase_list = grouperase_list + comperase_list
			@maingroup.entities.erase_entities erase_list
			
			tempinstance = @maingroup.entities.add_instance tempdef, temptrans


			tempinstance.explode
			erasestatus = @tempmaingroup.erase!

		end
	else

		# Creates time stamp for new assembly group name

		current_time = Time.now
		formatted_time = current_time.strftime("%Y%m%d%H%M%S")

		@maingroup = Sketchup.active_model.entities.add_group(@all_subgroups)
		@maingroup.name = "FLOOR_POLYGON_ASSEMBLY_#{formatted_time}"

		@maingrouptrans = @maingroup.transformation
		@maingrouporigin = @maingrouptrans.origin


		
	end
end

I don’t see where this is wrappped in an operation. Could that be higher in the callstack?

Perhaps a “wait tool” that displays an hourglass cursor (or whatever) that prompts the user to wait? Then pop it off the toolstack when the geometry is done being generated.

Using the same popping idea, whenever you go into regen mode, attach a ToolsObserver that detects the ZoomTool, PanTool or OrbitTool and pops it off the toolstack immediately.
When the regen is done, detach the tools observer.

1 Like

Correct, this is the final step in bringing all the sub groups that are created into one assembly “group”, the actual operation wrapping is done in a method that calls this final method.

1 Like

I’m trying to understand if anyone else (developers) have encountered this. Granted most plugins probably don’t do quite as much geometry generation as my Wall or Truss plugin do but some of them probably do, so if this was a limitation of SketchUp I’m running into I would like to find this out. If not then I should be able to make things a bit more robust and less prone to these specific crashses.

Here is the response to my original post (questions) from ChatGPT 5.2 Thinking:

What you’re describing is very consistent with a native-side crash caused by “camera updates happening while a big edit transaction is actively mutating lots of entities”—and it can absolutely present as pure BugSplat with no Ruby Console output. Ruby exceptions won’t catch this because the process is dying in SketchUp’s C++/render/driver/input stack, not in Ruby.

There are two overlapping risk factors in your report:

  1. Large, bursty geometry mutation (regen/redraw of an assembly with lots of groups/instances).
  2. Concurrent navigation input (mouse orbit/pan, and especially continuous 3Dconnexion camera input) during that mutation.

SketchUp is single-threaded, but that doesn’t prevent re-entrancy / timing-sensitive interactions between the UI/render loop and model changes—particularly when a device is continuously pushing camera transforms.

What to do in your code (highest leverage)

1) Wrap the entire regen in one operation with UI disabled

Make sure the regen is inside a single start_operation(..., true, ...) so SketchUp doesn’t redraw incrementally while entities are being created/erased/exploded.

The Ruby API explicitly supports this via disable_ui in Sketchup::Model#start_operation. (SketchUp Ruby API Documentation)

Pattern:

def with_undo(op_name, disable_ui: true)
  model = Sketchup.active_model
  model.start_operation(op_name, disable_ui, false, false) # disable_ui = true
  yield
  model.commit_operation
rescue => e
  model.abort_operation
  raise
ensure
  # If you disable UI updates, some inspectors may need a refresh:
  UI.refresh_inspectors
end

UI.refresh_inspectors is specifically noted as useful when UI updates were disabled. (SketchUp Ruby API Documentation)

Why this helps: it reduces the amount of “half-built geometry” that the renderer and selection/outliner/etc. might touch mid-flight.


2) Actively prevent navigation tools while regen is running

Dan Rathbun’s suggestion in the very thread you posted is solid: push a “wait tool” and/or attach a ToolsObserver that immediately pops Orbit/Pan/Zoom tools while you regenerate. (SketchUp Community)

Tool IDs for camera tools are listed in the ToolsObserver docs (Orbit 10508, Pan 10525, Zoom 10509, etc.). (SketchUp Ruby API Documentation)

Simple approach: attach a temporary ToolsObserver

class MedeekNoNavObserver < Sketchup::ToolsObserver
  NAV_TOOL_IDS = [10508, 10525, 10509, 10526, 10523].freeze # orbit, pan, zoom, zoom window, dolly

  def onActiveToolChanged(tools, tool_name, tool_id)
    if NAV_TOOL_IDS.include?(tool_id)
      tools.pop_tool
    end
  end
end

Then during regen:

  • model.tools.add_observer(obs)
  • run regen
  • model.tools.remove_observer(obs)

Caveat: 3Dconnexion navigation might not always show up as those camera tools (depending on how the 3Dx integration feeds camera changes), so this is not a guaranteed catch-all—but it does stop the classic mouse pan/orbit/zoom pathway, which you’ve already identified as a trigger.

More robust: push a “wait tool” that absorbs mouse/key events and makes it obvious the user should pause. That also matches your own “pause briefly” workaround, but enforced.


3) Avoid “regen while still inside a dialog callback” re-entrancy

If your Submit button is coming from an HtmlDialog callback, consider scheduling the heavy regen to run on the next tick:

UI.start_timer(0, false) { do_regen_now() }

This can reduce re-entrancy where UI is still unwinding from the click event while you begin destructive edits.

(You still want start_operation(..., true) and no-navigation during the actual regen.)


What’s not a Ruby “mistake,” but still worth tightening

  • Explode + erase + add_instance in a tight window is inherently stressful for SketchUp (lots of entity invalidation). That doesn’t mean your approach is wrong—parametric regen often requires it—but it’s exactly the kind of workload where “don’t let the camera move right now” matters most.
  • If you’re calling view.refresh repeatedly inside rebuild loops, try to remove that and refresh only once after commit. (Frequent refreshes increase the chance of intersecting camera redraw with half-mutated state.)

Why Windows 7 (and even older SketchUp) makes this worse

Windows 7 is long outside current support/testing, and SketchUp explicitly notes older Windows versions are unsupported, and that behavior may be untested. (help.sketchupaustralia.com.au)
So your “old machine Win7 SU2017–2020” crashes are very plausibly an environment/driver/render-stack fragility that your workflow triggers more easily.

What to ask your affected user to do (practical triage)

  • Update 3Dconnexion drivers / try a different driver version if the crashes correlate strongly with the SpaceMouse (there are multiple reports of SketchUp instability tied to 3Dconnexion components). (SketchUp Community)
  • Have them submit BugSplats with a clear comment like: “Medeek Wall – regen after Submit while SpaceMouse moving camera” (SketchUp can correlate crash signatures if the report text is consistent).

Bottom line

This is very likely an inherent stability edge-case in SketchUp’s native side that’s being triggered by “navigation input during heavy model mutation.” You can’t fully “fix” that from Ruby, but you can dramatically reduce the trigger surface by:

  • One operation, UI disabled during regen (start_operation(..., true, ...)) (SketchUp Ruby API Documentation)
  • Temporarily block navigation (wait tool + ToolsObserver) (SketchUp Community)
  • Kick regen to next UI tick if initiated from a dialog callback

If you want, paste your regen wrapper method (the one that calls your create_*_assembly / edit path) and I’ll point out exactly where to place the “disable_ui + no-nav window” with minimal changes.

I trust AI for some things (minor coding tasks) but big picture stuff like this makes me a bit leery.

Any thoughts on what ChatGPT has suggested or its recommendations. I’m fairly well versed in the API now but optimization and other advanced topics are still quite new to me.

I actually have since retired my Windows 7 machine (actually its OS hard drive finally died on Christmas day) and I have replaced my “old” workhorse with a Windows 10 machine that runs SU2017 and SU2026. I also have my even newer test machine which has SU2023 - SU2026 installed, running Windows 11.

It does seem familiar like I’ve perhaps seen a complaint before.

The following seems to work for preventing view navigation without resorting to an observer.

module Testing
  class PopToolTest
    # This is Windows only (svg filetype). Will pdf be found on Mac?
    # Meaning, that I am unsure if `find_support_file` looks inside the bundle.
     if Sketchup.find_support_file('dlg_agent_aimm_spinner.svg','Images')
      CURSOR_PATH = Sketchup.find_support_file('dlg_agent_aimm_spinner.svg','Images')
    else
      CURSOR_PATH = Sketchup.find_support_file('sketchup_loading.svg','Images')
    end
    CURSOR_ID = UI.create_cursor(CURSOR_PATH, 15, 15)
    # Init the tool instance.
    def initialize(*args)
      puts Sketchup.status_text = "Starting a PopToolTest"
    end
    # Stopping the tool instance.
    def deactivate(view)
      puts Sketchup.status_text = "Stopping the PopToolTest"
    end
    # Resume the tool instance.
    def resume(view)
      puts Sketchup.status_text = "Resuming the PopToolTest"
    end
    # This typically happens when a viewing tool is activated, such as when
    # orbit is activated due to the middle mouse button.
    def suspend(view)
      puts Sketchup.status_text = "Suspending the PopToolTest"
      UI.start_timer(0.1,false) { view.model.tools.pop_tool }
    end
    # Set the tool cursor.
    def onSetCursor
      UI.set_cursor(CURSOR_ID)
    end
  end

  def self.go
    Sketchup.active_model.select_tool(PopToolTest.new)
  end
end

EDIT: Changed to only create the cursor resource once.

Were you disabling the UI drawing during the operation or not?
(I do understand if you like allowing the user to see the geometry generation.)

1 Like