`SketchUp::require(path)` is broken on case-sensitive APFS

I reported this through Trimble Support, but I figured I’d cross-post it here.


I use SketchUp on a Mac with a case-sensitive filesystem.

When loading extensions, at some point in the process the path of the extension has all uppercase characters forcibly converted to lowercase characters.

As best I can tell, this bug is somewhere in the implementation of the SketchUp::require(path) method, as the bug traces through extensions.rb Line 197.

This is the Ruby console output I am seeing:

#<Errno::ENOENT: No such file or directory @ rb_sysopen - /users/elsiehupp/library/application support/sketchup 2025/sketchup/plugins/habitat_site_context/Resources/en-US.json>
/users/elsiehupp/library/application support/sketchup 2025/sketchup/plugins/habitat_site_context/localise.rbe:26:in `read'
/users/elsiehupp/library/application support/sketchup 2025/sketchup/plugins/habitat_site_context/localise.rbe:26:in `initialize'
/Users/elsiehupp/Library/Application Support/SketchUp 2025/SketchUp/Plugins/habitat_site_context.rb:9:in `new'
/Users/elsiehupp/Library/Application Support/SketchUp 2025/SketchUp/Plugins/habitat_site_context.rb:9:in `<module:HabitatSiteContext>'
/Users/elsiehupp/Library/Application Support/SketchUp 2025/SketchUp/Plugins/habitat_site_context.rb:4:in `<top (required)>'
#<Errno::ENOENT: No such file or directory @ dir_s_mkdir - /users/elsiehupp/library/application support/sketchup 2025/sketchup/plugins/su_diffusion/current_lib>
/users/elsiehupp/library/application support/sketchup 2025/sketchup/plugins/su_diffusion/su_loader.rbe:21:in `mkdir'
/users/elsiehupp/library/application support/sketchup 2025/sketchup/plugins/su_diffusion/su_loader.rbe:21:in `<module:SketchUpDiffusion>'
/users/elsiehupp/library/application support/sketchup 2025/sketchup/plugins/su_diffusion/su_loader.rbe:1:in `<main>'
/Applications/SketchUp 2025/SketchUp.app/Contents/Resources/Tools/extensions.rb:197:in `eval'
/Applications/SketchUp 2025/SketchUp.app/Contents/Resources/Tools/extensions.rb:197:in `require'
/Applications/SketchUp 2025/SketchUp.app/Contents/Resources/Tools/extensions.rb:197:in `load'
/Users/elsiehupp/Library/Application Support/SketchUp 2025/SketchUp/Plugins/su_diffusion.rb:22:in `register_extension'
/Users/elsiehupp/Library/Application Support/SketchUp 2025/SketchUp/Plugins/su_diffusion.rb:22:in `<module:SketchUpDiffusion>'
/Users/elsiehupp/Library/Application Support/SketchUp 2025/SketchUp/Plugins/su_diffusion.rb:5:in `<top (required)>'
#<Errno::ENOENT: No such file or directory @ rb_sysopen - >
/users/elsiehupp/library/application support/sketchup 2025/sketchup/plugins/su_sandbox/geometryhelpers.rbe:107:in `initialize'
/users/elsiehupp/library/application support/sketchup 2025/sketchup/plugins/su_sandbox/geometryhelpers.rbe:107:in `open'
/users/elsiehupp/library/application support/sketchup 2025/sketchup/plugins/su_sandbox/geometryhelpers.rbe:107:in `ReadValues'
/users/elsiehupp/library/application support/sketchup 2025/sketchup/plugins/su_sandbox/geometryhelpers.rbe:45:in `initialize'
/users/elsiehupp/library/application support/sketchup 2025/sketchup/plugins/su_sandbox/sandboxmenus.rbe:33:in `new'
/users/elsiehupp/library/application support/sketchup 2025/sketchup/plugins/su_sandbox/sandboxmenus.rbe:33:in `<module:SandboxTools>'
/users/elsiehupp/library/application support/sketchup 2025/sketchup/plugins/su_sandbox/sandboxmenus.rbe:24:in `<main>'
/Applications/SketchUp 2025/SketchUp.app/Contents/Resources/Tools/extensions.rb:197:in `eval'
/Applications/SketchUp 2025/SketchUp.app/Contents/Resources/Tools/extensions.rb:197:in `require'
/Applications/SketchUp 2025/SketchUp.app/Contents/Resources/Tools/extensions.rb:197:in `load'
/Users/elsiehupp/Library/Application Support/SketchUp 2025/SketchUp/Plugins/su_sandbox.rb:50:in `register_extension'
/Users/elsiehupp/Library/Application Support/SketchUp 2025/SketchUp/Plugins/su_sandbox.rb:50:in `<module:SandboxTools>'
/Users/elsiehupp/Library/Application Support/SketchUp 2025/SketchUp/Plugins/su_sandbox.rb:29:in `<top (required)>'
#<LoadError: cannot load such file -- LangHandler.rb>
<internal:/Applications/SketchUp 2025/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/3.2.2/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
<internal:/Applications/SketchUp 2025/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/3.2.2/lib/ruby/3.2.0/rubygems/core_ext/kernel_require.rb>:85:in `require'
/Users/elsiehupp/Library/Application Support/SketchUp 2025/SketchUp/Plugins/su_shadowstringsfix/shadowstringsfix_loader.rb:14:in `<top (required)>'
/Applications/SketchUp 2025/SketchUp.app/Contents/Resources/Tools/extensions.rb:197:in `require'
/Applications/SketchUp 2025/SketchUp.app/Contents/Resources/Tools/extensions.rb:197:in `load'
/Users/elsiehupp/Library/Application Support/SketchUp 2025/SketchUp/Plugins/su_shadowstringsfix.rb:39:in `register_extension'
/Users/elsiehupp/Library/Application Support/SketchUp 2025/SketchUp/Plugins/su_shadowstringsfix.rb:39:in `<module:ShadowStringsFix>'
/Users/elsiehupp/Library/Application Support/SketchUp 2025/SketchUp/Plugins/su_shadowstringsfix.rb:20:in `<top (required)>'

Note where, e.g., /Users/elsiehupp/Library/Application Support/SketchUp 2025 turns into /users/elsiehupp/library/application support/sketchup 2025.

Steam (the video game service) stupidly also does not support case-sensitive filesystems on macOS, and the known workaround is to create a sparsebundle (or APFS volume) with a case-insensitive filesystem, automount the sparsebundle (or APFS volume) at system startup, and symlink ~/Library/Steam to said mounted sparsebundle (or APFS volume).

A similar workaround could probably work with SketchUp; the path that gets fed into SketchUp::require(path) ultimately starts in each extension loader script’s __FILE__, so if the extensions are symlinked to a case-insensitive filesystem the loader should work.

Well, I tried that with /Users/elsiehupp/Library/Application Support/SketchUp 2025, and SketchUp freaked out and crashed on launch.

So I tried just symlinking /Users/elsiehupp/Library/Application Support/SketchUp 2025/SketchUp/Plugins, and lo and behold it worked!~

Which I guess is to say the problem apparently is indeed a bug with SketchUp::require(path) not working properly on case-sensitive filesystems, ~and symlinking just the Plugins directory to a case-insensitive APFS volume is an available workaround.

(It would still be nice if Trimble fixed this bug, though.)

EDIT: Actually, no, the workaround didn’t work. SketchUp just doesn’t see the symlinked Plugins directory, so none of the plugins fail to load… because SketchUp doesn’t even try to load them.

EDIT 2: Actually, yes, it does work, but only if you create a symlink in the terminal using, e.g.:

mv ~/Library/Application Support/SketchUp 2025/SketchUp/Plugins /Volumes/SketchUp/Plugins
ln -s /Volumes/SketchUp/Plugins ~/Library/Application Support/SketchUp 2025/SketchUp/Plugins

My mistake was being lazy and creating an “alias” in the Finder, which apparently isn’t the same thing as a symlink.

Edit 3: With the symlink to the Plugins folder, SketchUp doesn’t try to load extensions, but the Extension Manager still sees them and thinks they’ve been loaded. So maybe using a proper symlink (i.e. not a Finder alias) with the entire ~/Library/Application Support/SketchUp 2025 folder might work. I’m tired, so I’ll try this later.

I should add: the Apple KB article on APFS volumes.

(The sparsebundle approach is probably more applicable for deployment environments; for instance, the Plugins folder could be a read-only .dmg file rather than a writeable sparsebundle.)

Correct. This is by design. It is not actually a bug. Sketchup::require downcases the path when it pushes it into the $LOADED_FEATURES (aka $") array.

This was done (in hopes) of not doing what Kernel#require does on a case-insensitive filesystem when mixed case filenames are passed. For example, arguments "sketchup.rb", "Sketchup.rb" and "SKETCHUP.rb" each load or reload the "Tools/sketchup.rb" file because the Kernel#require method is doing case-sensitive string comparisons against the members of the $" array.

I sort of feel that the API should have overridden the Kernel#require method to fix the issue there for case-insensitive filesystems.
ADD: This later got more complicated when SketchUp began pseudo-supporting RubyGems which further overrides the global require method.

AFAIK (relying upon memory,) the API is not supported for running on case-sensitive OS installations. I would surmise that you’d be a long time in waiting for a solution to this particular scenario.


@TedVitale_SU | @CraigTrickett

  • Can the System Requirements Support page and the API documentation pages be updated to address case-sensitive installations for the Mac editions, please?
1 Like

The easiest possible fixes, I would think is to:

  1. Add a hash (named) argument to Sketchup::require to control the the downcasing of paths. The API should determine the OS filesystem’s case sensitivity and set the default Boolean value for this extra argument accordingly.
    An API coder could explicitly override the default when necessary.

  2. This setting could (also) be exposed as an application level Boolean property at Sketchup::case_senstive? and Sketchup::case_senstive= which could allow coders to temporarily change the behavior during loading their code.

Sorry it’s taken me a while to respond.

One thing I had been meaning to test was putting puts $" in the SketchUp Ruby Console. Having done so, I got the following:

> puts $"
enumerator.so
thread.rb
rational.so
complex.so
ruby2_keywords.rb
encdb.so
trans/transdb.so
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/x86_64-darwin18/rbconfig.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/compatibility.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/defaults.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/deprecate.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/errors.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/version.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/requirement.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/platform.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/basic_specification.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/stub_specification.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/util.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/text.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/user_interaction.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/specification_policy.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/util/list.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/specification.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/exceptions.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/bundler_version_finder.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/dependency.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/core_ext/kernel_gem.rb
monitor.so
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/monitor.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/core_ext/kernel_require.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/core_ext/kernel_warn.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/rubygems/path_support.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/did_you_mean/version.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/did_you_mean/core_ext/name_error.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/did_you_mean/levenshtein.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/did_you_mean/jaro_winkler.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/did_you_mean/spell_checker.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/did_you_mean/spell_checkers/name_error_checkers/class_name_checker.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/did_you_mean/spell_checkers/name_error_checkers/variable_name_checker.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/did_you_mean/spell_checkers/name_error_checkers.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/did_you_mean/spell_checkers/method_name_checker.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/did_you_mean/spell_checkers/key_error_checker.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/did_you_mean/spell_checkers/null_checker.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/did_you_mean/formatters/plain_formatter.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/did_you_mean/tree_spell_checker.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Frameworks/Ruby.framework/Versions/2.7.1/lib/ruby/2.7.0/did_you_mean.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Resources/Content/Tools/extensions.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Resources/Content/Tools/langhandler.rb
/Applications/SketchUp 2021/SketchUp.app/Contents/Resources/Content/Tools/sketchup.rb
/users/elsiehupp/library/application support/sketchup 2021/sketchup/plugins/su_advancedcameratools/actloader.rbe
/users/elsiehupp/library/application support/sketchup 2021/sketchup/plugins/su_advancedcameratools/advancedcameratools_main.rbe
/Users/elsiehupp/Library/Application Support/SketchUp 2021/SketchUp/Plugins/su_advancedcameratools.rb
/users/elsiehupp/library/application support/sketchup 2021/sketchup/plugins/su_dynamiccomponents/ruby/dcloader.rbe
/users/elsiehupp/library/application support/sketchup 2021/sketchup/plugins/su_dynamiccomponents/ruby/dcobservers.rbe
/users/elsiehupp/library/application support/sketchup 2021/sketchup/plugins/su_dynamiccomponents/ruby/dcclass_v1.rbe
/users/elsiehupp/library/application support/sketchup 2021/sketchup/plugins/su_dynamiccomponents/ruby/dcfunctions_v1.rbe
/users/elsiehupp/library/application support/sketchup 2021/sketchup/plugins/su_dynamiccomponents/ruby/dcclassifier.rbe
/users/elsiehupp/library/application support/sketchup 2021/sketchup/plugins/su_dynamiccomponents/ruby/dcclass_overlays.rbe
/users/elsiehupp/library/application support/sketchup 2021/sketchup/plugins/su_dynamiccomponents/ruby/dctools.rbe
/users/elsiehupp/library/application support/sketchup 2021/sketchup/plugins/su_dynamiccomponents/ruby/dcutils.rbe
/users/elsiehupp/library/application support/sketchup 2021/sketchup/plugins/su_dynamiccomponents/ruby/dcconverter.rbe
/users/elsiehupp/library/application support/sketchup 2021/sketchup/plugins/su_dynamiccomponents/ruby/generatereport.rbe
/Users/elsiehupp/Library/Application Support/SketchUp 2021/SketchUp/Plugins/su_dynamiccomponents.rb
/users/elsiehupp/library/application support/sketchup 2021/sketchup/plugins/su_sandbox/sandboxmenus.rbe
/users/elsiehupp/library/application support/sketchup 2021/sketchup/plugins/su_sandbox/geometryhelpers.rbe
/Users/elsiehupp/Library/Application Support/SketchUp 2021/SketchUp/Plugins/su_sandbox.rb
/users/elsiehupp/library/application support/sketchup 2021/sketchup/plugins/su_trimble_connect/boot.rbe
/Users/elsiehupp/Library/Application Support/SketchUp 2021/SketchUp/Plugins/su_trimble_connect.rb

It appears there is a mix of correctly cased and forced-downcase strings in this array.

It’s hard for me to understand the rationale for the behavior because:

a. I am not a Ruby developer, and
b. I don’t have access to the internal implementation code of SketchUp::require(path).

The big difference I did notice, though, was that Kernel#require takes a Pathname object rather than just a string. So maybe the “correct” fix for this behavior could be SketchUp::require(path) using a Pathname object instead of a string, as well.

Or, for instance, SketchUp::require(path) could force-downcase a copy of the string for comparison, or it could use some other method for detecting duplicates, such as, I dunno, the Unix file descriptor.

Or, perhaps, SketchUp for Mac could use something like MacFUSE to create a case-insensitive userspace filesystem for its Ruby engine, as opposed to expecting the human user to accommodate it.

At the very least, though, it should not have taken me this much work to get to the point of understanding that SketchUp doesn’t like case-sensitive filesystems. There should have been multiple warnings along the way, starting with, e.g., the SketchUp application installer and continuing with an explicit error within the Ruby console stating that the underlying problem is the case-sensitive filesystem, rather than just trying and failing to use the forced-downcase version of the path.

Yes, I suppose it’s nice that SketchUp doesn’t straight-up refuse to run on a case-sensitive filesystem, but, again, there should have been multiple advance warnings of this limitation, rather than weird, opaque errors.

I’ll continue trying workarounds with symlinks, though.

I think I figured out a workaround that actually works.

First, yes, I created an APFS volume at /Volumes/SketchUp with a case-insensitive filesystem. On this volume, I created a folder at /Volumes/SketchUp/Applications and a nested pair of folders at /Volumes/SketchUp/Library/Application Support (because of potential name collisions).

Then, after running SketchUp 2025 at least once (so it has an Application Support folder), I moved ~/Library/Application Support/SketchUp 2025 to /Volumes/SketchUp/Library/Application Support/SketchUp 2025 and I moved /Applications/SketchUp 2025 to /Volumes/SketchUp/Applications/SketchUp 2025, then created a symlink as follows:

ln -s /Volumes/SketchUp/Library/Application\ Support/SketchUp\ 2025 ~/Library/Application\ Support/SketchUp\ 2025

The reason I thought to move the application itself onto the case-insensitive volume was that when I did puts $" a number of the paths were within SketchUp.app, and SketchUp.app had been on my boot volume, which was case-sensitive.

For a visual representation, the contents of the volume now look like so:

Note: The reason I used this folder structure was that just putting both the application and the application-support folder in the volume’s root cause a name collision, and the Finder wanted to merge the two folders, which kind of seemed like a bad idea.

The precise names I used for the folders aren’t actually meaningful to the application itself; they are just there to help remind me, the end user, which is which.

Putting both the Application Support folder and the application itself on the case-insensitive volume seems to have done the trick.

One weird thing perhaps worth mentioning, though, is that the application didn’t seem to recognize the extensions until I installed or uninstalled an extension, which I guess forced the application to try and resolve the extensions again. The way this manifested in the interface was that before I did so the extensions in the Extension Manager all had generic icons and lacked their full names, whereas after I installed or uninstalled an extension the extensions in the Extension manager displayed with the correct icons and display names.

1 Like

Note: I merged this comment into the one preceding it.

It is not that hard to understand. If the path string points to a plain-Jane rb file, then SketchUp::require will simply call Kernel require passing along the string unchanged.
But if the file is an encrypted rbe or rbs, then SketchUp::require will open and decrypt the file and evaluate the results on the C-side. (I.e., Ruby itself is coded in C.) If the file’s code evaluates successfully, then SketchUp::require will downcase the path string and push into onto the $" array.

The word rather in your comment makes the statement untrue. The correct term is accepts a Pathname as well as a String.

But (I’m not sure I made this plain earlier,) it is not actually Kernel#require that is called. Rubygems overrides it and redefines this method. If we click the method description in the Ruby Core docs, we can see the code is from the Rubygems extension and that the code will “duck type” the path argument to see if it has a #to_path method, which would type identify it as a Pathname object and then call #to_path on it (which is just an alias for #to_s,) thereby converting the argument to a String object if it is a Pathname object. Otherwise it is assumed the argument is a String object. Viz:

Ruby code for global `require` method as redefined by RubyGems (click to expand) ...
# File lib/rubygems/core_ext/kernel_require.rb, line 37
def require(path) # :doc:
  return gem_original_require(path) unless Gem.discover_gems_on_require

  begin
    RUBYGEMS_ACTIVATION_MONITOR.enter

    path = path.to_path if path.respond_to? :to_path

    if spec = Gem.find_unresolved_default_spec(path)
      # Ensure -I beats a default gem
      resolved_path = begin
        rp = nil
        load_path_check_index = Gem.load_path_insert_index - Gem.activated_gem_paths
        Gem.suffixes.each do |s|
          $LOAD_PATH[0...load_path_check_index].each do |lp|
            safe_lp = lp.dup.tap(&Gem::UNTAINT)
            begin
              if File.symlink? safe_lp # for backward compatibility
                next
              end
            rescue SecurityError
              RUBYGEMS_ACTIVATION_MONITOR.exit
              raise
            end

            full_path = File.expand_path(File.join(safe_lp, "#{path}#{s}"))
            if File.file?(full_path)
              rp = full_path
              break
            end
          end
          break if rp
        end
        rp
      end

      begin
        Kernel.send(:gem, spec.name, Gem::Requirement.default_prerelease)
      rescue Exception
        RUBYGEMS_ACTIVATION_MONITOR.exit
        raise
      end unless resolved_path
    end

    # If there are no unresolved deps, then we can use just try
    # normal require handle loading a gem from the rescue below.

    if Gem::Specification.unresolved_deps.empty?
      RUBYGEMS_ACTIVATION_MONITOR.exit
      return gem_original_require(path)
    end

    # If +path+ is for a gem that has already been loaded, don't
    # bother trying to find it in an unresolved gem, just go straight
    # to normal require.
    #--
    # TODO request access to the C implementation of this to speed up RubyGems

    if Gem::Specification.find_active_stub_by_path(path)
      RUBYGEMS_ACTIVATION_MONITOR.exit
      return gem_original_require(path)
    end

    # Attempt to find +path+ in any unresolved gems...

    found_specs = Gem::Specification.find_in_unresolved path

    # If there are no directly unresolved gems, then try and find +path+
    # in any gems that are available via the currently unresolved gems.
    # For example, given:
    #
    #   a => b => c => d
    #
    # If a and b are currently active with c being unresolved and d.rb is
    # requested, then find_in_unresolved_tree will find d.rb in d because
    # it's a dependency of c.
    #
    if found_specs.empty?
      found_specs = Gem::Specification.find_in_unresolved_tree path

      found_specs.each do |found_spec|
        found_spec.activate
      end

    # We found +path+ directly in an unresolved gem. Now we figure out, of
    # the possible found specs, which one we should activate.
    else

      # Check that all the found specs are just different
      # versions of the same gem
      names = found_specs.map(&:name).uniq

      if names.size > 1
        RUBYGEMS_ACTIVATION_MONITOR.exit
        raise Gem::LoadError, "#{path} found in multiple gems: #{names.join ', '}"
      end

      # Ok, now find a gem that has no conflicts, starting
      # at the highest version.
      valid = found_specs.find {|s| !s.has_conflicts? }

      unless valid
        le = Gem::LoadError.new "unable to find a version of '#{names.first}' to activate"
        le.name = names.first
        RUBYGEMS_ACTIVATION_MONITOR.exit
        raise le
      end

      valid.activate
    end

    RUBYGEMS_ACTIVATION_MONITOR.exit
    return gem_original_require(path)
  rescue LoadError => load_error
    if load_error.path == path
      RUBYGEMS_ACTIVATION_MONITOR.enter

      begin
        require_again = Gem.try_activate(path)
      ensure
        RUBYGEMS_ACTIVATION_MONITOR.exit
      end

      return gem_original_require(path) if require_again
    end

    raise load_error
  end
end

We can further prove this by passing an argument of an invalid type, like an integer:

require 4
#<TypeError: no implicit conversion of Integer into String>
<internal:C:/Program Files/SketchUp/SketchUp 2026/SketchUp/Tools/RubyStdLib/rubygems/core_ext/kernel_require.rb>:85:in `require'

As shown above it is not actually a big difference and it’s not a “fix” because the global require is using a String internally anyway.

So it is not what is going in (the argument) that is the issue. It’s the SketchUp::require changing the casing when it is an encrypted file. The casing of the paths pushed into $" just causes the reloading of encrypted files.

Agreed. The documentation is lacking in this regard.

However, the Mac edition does not use an installer. It uses a DMG image.
Regardless, the application itself could have a routine in it’s startup that displays a warning dialog.

Good job! It think that this what what others also found in the related topics.

Maybe you could write up a blog doc of something for others?

tl;dr the very simple workaround is to move both SketchUp.app and its Application Support folder to a case-insensitive APFS volume:

  1. Run SketchUp at least once, so that it creates an Application Support folder.
  2. Create a case-insensitive APFS volume called, e.g., /Volumes/SketchUp. See the Apple KB article on APFS volumes. (There’s probably a way to do this using the Terminal if you want to be clever about it.)
  3. Open Terminal and run the following commands (see screenshot for what this should look like):
mkdir /Volumes/SketchUp/Applications
mkdir -p /Volumes/SketchUp/Library/Application\ Support
mv /Applications/SketchUp\ 2025 /Volumes/SketchUp/Applications/SketchUp\ 2025
mv ~/Library/Application\ Support/SketchUp\ 2025 Volumes/SketchUp/Library/Application\ Support/SketchUp\ 2025
ln -s /Volumes/SketchUp/Library/Application\ Support/SketchUp\ 2025 /Users/elsiehupp/Library/Application\ Support/SketchUp\ 2025
  1. Launch SketchUp and open Extensions > Extension Manager. If the extensions have generic icons:
    a. Open Extensions > Extension Warehouse and install any extension at random.
    b. Go back to the Extension Manager and uninstall the extension you just installed if you don’t actually need it.
    This install/uninstall will force SketchUp to reload all extensions.

It seems that you may need to do the install/uninstall rigmarole every single time you launch SketchUp, which is annoying, but it’s better than having extensions simply not work.

Also, note, obviously SketchUp 2025 in the Terminal commands is for a particular version; you will need to change each occurrence to, e.g., SketchUp 2026 if that’s the version you’re working with.

2 Likes

Maybe you could write up a blog doc of something for others?

Hopefully my tl;dr which I’ve marked as the solution is clear enough.

(Of course it would be nice if SketchUp figured all these things out itself, e.g. by changing the behavior of SketchUp::require(path) or doing something clever with, e.g., MacFUSE.)

1 Like

Yup it will appear at the top of the thread now.