[SOLVED] How can a Blender Python script crash randomly while all parameters are exactly the same every time?

Hey everyone!

I’ve got a really weird problem, and really don’t know how to tackle it anymore. I have created an operator that copies the active material to decals from DecalMachine. It was quite hard to even replicate the crash at first, since it only crashed very rarely, but I found a way to replicate the crash by saving a scene with a certain material and certain decals and then running the operator.

Now here is the weird part: I repeat the exact same actions every time: I open the blend file and immediately run the operator, nothing else is touched or changed. And then, sometimes it crashes and sometimes it doesn’t… I was trying to find a pattern before about when it crashed, but it seems to be totally random since in my latest tests everything is the exact same, even the selection order is saved in the blend file… Sometimes it doesn’t crash for 8 straight tries and then the 9th time it crashes anyways.

My main question is: How can a python script crash so randomly while all parameters are exactly the same every time?

My first hunch was that the script was accessing data-blocks that were deleted, but after removing all lines that delete data-blocks it still crashes. And it wouldn’t make any sense that it crashes randomly then anyways

The code for the operator can be found here: https://github.com/OliverJPost/Decal_Matching_InstaMat/blob/main/SMR_DECALMACHINE.py

The crash log for one of the crashes can be found here:

Sometimes it backtraces to line 223, other times to 345. I’ve tried putting these lines in try/excepts even though it didn’t make any sense to me that these lines would be the cause anyways.

Let me know if you have any advice for where I could start trying to fix this

Regards,
Oliver

# Python backtrace
  File "C:\Users\Ole\AppData\Roaming\Blender Foundation\Blender\2.90\scripts\addons\SMR_PRO\SMR_DECALMACHINE.py", line 223 in connect_outputs
  File "C:\Users\Ole\AppData\Roaming\Blender Foundation\Blender\2.90\scripts\addons\SMR_PRO\SMR_DECALMACHINE.py", line 457 in execute

The trace says line 223.

I would perhaps start by removing the bindings and use full paths to see if it makes a difference. I don’t know how blender reallocates node links, but if it’s anything like bpy_prop_collections, holding a reference to an element in a collection while extending the collection itself is bad and is one of the official gotchas.

Eg. instead of:

group_nodes[source]

do:

group.nodes[source]

And

output_node.inputs[output_socket]

to:

group.nodes['Output'].inputs[output_socket]

etc.

Thank you for this suggestion! I will go over all of these references and making them absolute and see if it makes a difference.
I was aware of the Gotcha that you need to be careful with removing things from these kind of references and afterwards using the reference again, but is this the same case when adding to them?

https://docs.blender.org/api/current/info_gotcha.html#help-my-script-crashes-blender

Some container modifications are actually safe, because they will never re-allocate existing data (e.g. linked lists containers will never re-allocate existing items when adding or removing others).

But knowing which cases are safe and which aren’t implies a deep understanding of Blender’s internals. That’s why, unless you are willing to dive into the RNA C implementation, it’s simpler to always assume that data references will become invalid when modifying their containers, in any possible way.

That’s the general guideline, but it roughly translates to:

thing = container["my_thing"]

for i in range(100):
    new_thing = container.add()
    new_thing.parent = thing.name

During the loop, (unless we know for sure) we can’t assume that thing is valid after adding new entries because blender may dynamically move container to a new place in memory in an effort to make the internal arrays contiguous, while the python binding references the thing in the old memory location.

Yeah, okay, that makes sense. So maybe that could also be why it crashes randomly. Based on if it decides to move it to another place in memory or not, which could be different every time. I’ll let you know what I find

Thanks!

1 Like

Okay, so I’ve tested a lot of stuff and eventually got it working. It seems that your suggestion was not in fact the thing that was crashing Blender, but it did help me in the right direction. Apparently it had something to do with connecting to the node_group inputs and outputs dynamically. I was able to overcome this with changing:

group.links.new(corresponding_node.outputs[socket], output_node.inputs[output_socket])

To:

socket_out = group.path_resolve('nodes["Output"].inputs[{}]'.format(output_socket))
group.links.new(corresponding_node.outputs[socket], socket_out)

I’m still not sure why this fixed it. The solution is based on this post: Building NodeGroups with Python crashes blender

Path resolving does some magic under the hood, but the fact that it works and regular python attribute access doesn’t (bpy_struct __getattribute__ is overridden, though), is disconcerting. Hopefully an isolated edge case.

1 Like