Skip to content

Bonsai: fix Link IFC silently failing to build its cache - #9360

Draft
BIMvoice wants to merge 1 commit into
IfcOpenShell:v0.8.0from
BIMvoice:fix-link-ifc-silent-failure
Draft

Bonsai: fix Link IFC silently failing to build its cache#9360
BIMvoice wants to merge 1 commit into
IfcOpenShell:v0.8.0from
BIMvoice:fix-link-ifc-silent-failure

Conversation

@BIMvoice

Copy link
Copy Markdown
Contributor

Linking an IFC can silently do nothing: no geometry appears, no error is shown, and the operator still reports success. Reported by a user who noticed that a cache file older versions created was no longer being written.

Root cause

LoadLink.link_ifc() spawns a headless Blender subprocess to build the .ifc.cache.blend that linking then loads. That subprocess is started with:

blender -b --addons <package> --python <generated script>

--addons registers the addon's classes but does not populate bpy.context.preferences.addons. So in the child process, anything that reads addon preferences raises:

KeyError: 'bpy_prop_collection[key]: key "bonsai" not found'
  ... get_addon_preferences -> bpy.context.preferences.addons[blender_package_name]

The child dies, the .blend is never written, and linking has nothing to load.

The path into that call is load_pset_templates -> get_user_data_dir (tool/blender.py:2148) -> get_addon_preferences().

Why it is version dependent

That get_user_data_dir call arrived with c91fbc4d40 "Setup user data dir" (2025-01-21). Bonsai before that commit never touched addon preferences in the child, so the cache was written and linking worked. Afterwards it can fail. That matches the report of "older versions created this file, newer ones do not" precisely.

It does not fail for everyone, which is why it has been hard to pin down. It needs the addon to be enabled in the current session but not yet persisted to saved preferences: a fresh enable, a first install, or a new profile. Once preferences have been saved, the key exists and the child survives.

The silent part, which is arguably the worse bug

if run.returncode == 1:
    print("An error occurred while processing your IFC.")
    if not blend_filepath.exists() or blend_filepath.stat().st_mtime < t:
        return {"CANCELLED"}

A print to a console most users never open, and no self.report(). subprocess.run also did not capture output, so the child's traceback, which names the real cause, was discarded. The user sees a click that does nothing.

Changes

Enable the addon properly in the subprocess. addon_utils.enable(name, default_set=True, persistent=False) inside the generated script, so preferences exist for it to read. persistent=False so nothing is written to the user's saved preferences.

Check the cache directory is writable before spawning, with a message that names the likely culprits:

Cannot link 'model.ifc': the folder '...' is not writable (Permission denied). Linking needs to create a cache file next to the IFC. Move the file to a writable, non-synced location (not a read-only, network, or cloud-synced folder like OneDrive/Dropbox) or fix its permissions.

The cache still lives next to the IFC. That behaviour is unchanged, since other code and users depend on it.

Use {value!r} instead of "{value}" throughout the generated script. Paths and property values were interpolated into string literals, so a quote, a trailing backslash or awkward characters could produce a syntactically broken script. repr() always yields a valid escaped literal.

Removed h5_filepath. Declared at line 1579 and never read anywhere in the tree. It is already gone on v0.9.0.

Verification

Reproduced live in Blender 5.2 with an isolated profile and a real headless subprocess, not a simulation.

Before, with the cache directory set read-only:

KeyError: 'bpy_prop_collection[key]: key "bonsai" not found'
An error occurred while processing your IFC.
OPERATOR RESULT: {'FINISHED'}

Nothing reported, operator claims success.

After, same conditions:

Error: Cannot link 'test_project.ifc': the folder '...' is not writable
([Errno 13] Permission denied: ...). Linking needs to create a cache file next to the IFC.

And in a writable directory after the fix: subprocess returns 0, .ifc.cache.blend and .ifc.cache.json are written, operator finishes.

black and ruff clean.

Scope honesty

No automated test. Exercising this needs a real headless Blender subprocess with the full dependency set, which is not currently wired up for this module. Verification is the manual reproduction above, so nothing guards against regression in CI. If maintainers would like a test, guidance on the preferred harness would be welcome.

Left deliberately unchanged: LinkIfc._execute() calls bpy.ops.bim.load_link(...) and discards the result, so the outer operator reports FINISHED even when the nested one fails. The error message still surfaces, so this does not block the fix, but the top-level status does not reflect partial failure in a multi-file batch. That felt like a separate change with its own risk, so it is flagged rather than bundled.

Produced with AI assistance.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant