I have made an addon that when installed on Windows works perfectly fine. But when installed on Blender running on Mac OS, the binary contained within the addon zip file loses the execute permission. Here to prove that the execute permissions in -rwxrwxrwx@ were there before installation, when zip was downloaded and extracted into /Users/user/Downloads/:

user@macbook % pwd
user@macbook % ls -la
total 8840
drwxrwxrwx@ 3 user  staff       96 Jan 19 09:46 .
drwxrwxrwx@ 6 user  staff      192 Jan 19 09:46 ..
-rwxrwxrwx@ 1 user  staff  4523992 Jan 18 01:19 executable-binary

But when I install the addon zip file in Blender running on Mac OS and check the directory /Users/user/Library/Application Support/Blender/4.0/scripts/addons/ where Blender puts the addon, I noticed that the binary has lost its execute permission in -rw-r--r-- and that's when my addon fails to function properly.

user@macbook % pwd
/Users/user/Library/Application Support/Blender/4.0/scripts/addons/addon-name/executables/0.0.1/darwin/myexe.app/Contents/MacOS
user@macbook % ls -la                   
total 8840
drwxr-xr-x  3 user  staff       96 Jan 19 14:26 .
drwxr-xr-x  6 user  staff      192 Jan 19 14:26 ..
-rw-r--r--  1 user  staff  4523992 Jan 19 14:26 executable-binary

My current workaround is to manually add the execute permissions to the binary using chmod +x executable-binary. All the other parent directories still have retained the execute permissions. It's just the binary that has lost it. I'm uncertain whether this behavior is a bug or intentional, but it seems unlikely to be intentional since it results in inconsistency, especially considering that it functions correctly on Windows. So my question is how do I properly deploy and install such an addon that uses third party executable binaries? So that I can properly communicate that to the users via documentation.

  • 1
    $\begingroup$ Can confirm and I personally guess this is intentional to prevent any execution of malware but I may be wrong... so I'd suggest to report this behavior to make sure. In the meantime, you can make it executable automatically in the register() call, see my basic setup (tested on 4.0) based on a rather old answer. Does this help? $\endgroup$
    – p2or
    Commented Jan 23 at 16:02
  • $\begingroup$ @p2or hi thank you for looking into this! interesting i totally forgot to check if there was a script. but wouldn't os.chmod(executable_path, 0o755) be enough? $\endgroup$
    – Harry McKenzie
    Commented Jan 24 at 7:06
  • $\begingroup$ i think this is the solution/workaround to my issue. can you add that as an answer. in the meantime i will borrow a mac to test this. but i guess if this works on Windows Ubuntu app then it must definitely work on Mac i suppose? $\endgroup$
    – Harry McKenzie
    Commented Jan 24 at 7:16
  • 1
    $\begingroup$ No problem. Yep, os.chmod(app, 0o755) works as well (tested on OSX), just wanted to use 'Pathlib' and provide a cross-platform solution: docs.python.org/3/library/pathlib.html#pathlib.Path.chmod $\endgroup$
    – p2or
    Commented Jan 24 at 7:30
  • $\begingroup$ awesome thank you so much for the save! please add as answer and i will award the bounty and i can close this question :) $\endgroup$
    – Harry McKenzie
    Commented Jan 24 at 7:47

1 Answer 1


The reason for this issue can be found in scripts/startup/bl_operators/userpref.py.

Addons provided as zip files are extracted using ZipFile.extractall(path=None, ...), see line 688. As part of the extraction process, shutil.copyfileobj() is getting called internally, which copies each file to the destination in an exemplary manner, but unfortunately without the execution bits.

However, you can make the file executable again within the register() call of your addon by using either the pathlib or the os module in combination with the stat module:


# Path to the app
from pathlib import Path
app = Path(__file__).parent / "name-of-executable"

classes = (

def register():
    from bpy.utils import register_class
    for cls in classes:
    import stat
    app.chmod(app.stat().st_mode | stat.S_IEXEC)
    #os.chmod(str(app), st.st_mode | stat.S_IEXEC)

def unregister():
    from bpy.utils import unregister_class
    for cls in reversed(classes):

Full demo to capture the output of a C++ program supplied by an addon, based on Use array values from a C++ program in blender (you just need to compile random-numbers.cpp and zip both files).


You must log in to answer this question.

Not the answer you're looking for? Browse other questions tagged .