Friday, June 13, 2025

Debugging your Audio Unit plugin with auvaltool (aka auval)

Sudara

Sudara is building Sine Machine, an additive synth in JUCE, writing about technical challenges along the way.

https://melatonin.dev

For an Audio Unit v2 plugin to show up in DAWs on the Mac like Apple's Logic Pro, it must pass an Apple validation tool named auvaltool.

That makes working with auvaltool pretty much mandatory at some point when shipping cross-platform plugins.

Maybe you'll get lucky and not have to deal with it much. But for the rest of us, here's an in-depth guide.

auval vs. auvaltool?

A tiny bit of history: Older versions of macOS shipped with a tool called auval. It later became a wrapper script that ran auvaltool.

In more recent macOS versions (macOS >= 15) auval is just a symlink to auvaltool:

> ls -ahl /usr/bin/auval
lrwxr-xr-x  1 root  wheel    18B Apr 12 07:16 /usr/bin/auval -> /usr/bin/auvaltool

Pluginval runs auvaltool

An easy way to use auvaltool is via pluginval, a open-source meta-validator for all audio plugin formats built by Tracktion.

In addition to other tests, pluginval runs auval under the hood. I consider it mandatory for shipping cross-platform plugins and wrote more about it here.

To be precise, pluginval runs auval -strict by default (when pluginval's strictness-level is 5 or under).

It adds -stress 20 when strictness-level is above 5, which does the following:

run additional multi-thread stress test for N simulated seconds of audio I/O
good for catching race condition bugs
(default without specifying optional duration argument is 600 seconds)

Audio Units are registered to the system

Audio Units are unique in that macOS has a system registry for them. A service called AudioComponentRegistrar is in charge of registering plugins.

It keeps an eye on /Library/Audio/Plug-Ins/Components/ and the user version ~/Library/Audio/Plug-Ins/Components/ — when something new is detected in these folders, it adds any valid AudioUnits to an internal database.

The registrar can be a source of difficulty for plugin devs. Sometimes your plugin doesn't show up and you don't know why. You've probably seen this snippet floating around or even asked customers to run this in Terminal:

killall -9 AudioComponentRegistrar

It basically just re-triggers the registrar to run by turning it on and off again.

Calling auvaltool on the command line

Due to this system registry, you can't directly pass a path to a .component on the filesystem to auvaltool. Instead you refer to its 4 digit manufacturer and plugin code. Then auvaltool queries the registry to find it.

On the command line you'll specify -v TYPE SUBT MANU to validate one specific plugin.

This is what it looks like for me:

auval -strict -v aumu SM22 Mela

aumu is my TYPE. In this example, I have a musical instrument. If you have an audio effect it's aufx. It might also be aumf if you have an audio effect with instrument capabilities. You can find all the types here.

SM22 is my JUCE PLUGIN_CODE

MELA is my JUCE PLUGIN_MANUFACTURER_CODE

Figuring out your manufacturer and plugin code

You might remember these from when you first setup your JUCE plugin.

In CMake, they are the PLUGIN_MANUFACTURER_CODE and PLUGIN_CODE in your juce_add_plugin call.

In Projucer, they are in the main project settings (click the gear icon).

Debugging your plugin's code in auval

Both pluginval and auvaltool expose edge cases in your binary that you can't easily reproduce otherwise.

For example, auvaltool will run processBlock calls from the message thread. This might break a few things. Or you might have memory that isn't fully initialized at startup that gets exposed.

It's possible to run pluginval in a debugger and have it stop on breakpoints in your code.

Unfortunately, by default you can't attach a debugger to auvaltool. If you try, you'll see this:

process exited with status -1 (attach failed (Not allowed to attach to process.

This is because auvaltool doesn't have the correct entitlement to allow debugging,
com.apple.security.get-task-allow bool true.

> codesign -d --entitlements - /usr/bin/auval
Executable=/usr/bin/auvaltool
[Dict]
	[Key] com.apple.security.cs.allow-unsigned-executable-memory
	[Value]
		[Bool] true
	[Key] com.apple.security.cs.disable-library-validation
	[Value]
		[Bool] true
	[Key] com.apple.security.temporary-exception.audio-unit-host
	[Value]
		[Bool] true

In the past, the solution was to copy the binary into the user directory and adhoc codesign it, adding the required entitlement.

Unfortunately on recent versions of macOS, auvaltool is an ARMe build which has pointer authentication:

> lipo -archs /usr/bin/auvaltool
x86_64 arm64e

Pointer authentication encodes a signature into pointers. It mixes in a key derived from the binary’s code signature. So resigning breaks the binary and the trick of resigning and adding the debug entitlement no longer works.

Option 1: Use the x86_64 "slice"

The auvaltool binary is a universal binary, with both ARMe and x86_64 binaries in there. You can run the x86_64 binary, and assuming you built your .component for x86_64 as well, then you can debug that binary.

You'll still need to make a copy of the tool and code sign it so that you can debug it:

cp /usr/bin/auvaltool .
codesign -s - -f auvaltool # ahhoc sign
./auvaltool # this won't work on arm macs
arch -x86_64 ./auvaltool # but this works

This route works only works if you build your AU for x86_64. Also, be aware that Rosetta 2 will be sunset in Fall 2027 (macOS 28) which means this route will no longer work at some point in the future.

Related short story: I contacted Apple Code-level support (Developer Technical Support) to see what the workarounds might be and if they were aware of the problem.

They instructed me to file an issue via apple Feedback (which I did), but confirmed that one workaround is to run the x86_64 binary.

Option 2: Disable SIP in Safe Mode

Unless the CoreAudio team responds to my Feedback request, the only other route to debug AU plugins is to boot into Safe Mode and disable System Integrity Protection.

To boot into Safe Mode on Apple Silicon:

  1. Turn your mac off.
  2. Press and hold the power button on your Mac until the system volume and the Options button appear.
  3. Click Options > Continue > Select your volume and log in as an admin.
  4. From the menu bar select Utilities > Terminal.
  5. Type csrutil disable and when it's done, reboot.

After this, you will be able to debug /usr/bin/auvaltool itself with no need to copy or make modifications.

Have your IDE debug auvaltool

Once you've dealt with SIP, it's pretty easy to tell your IDE to run and attach to auvaltool in the debugger. You'll tell it to attach to /usr/bin/auvaltool directly if you've disabled SIP, otherwise point it to your copied version of the binary. Pass it the arguments that identify your plugin.

Assuming you have your arguments in order, you should be able to set breakpoints and see assertions:

Errors and auvaltool

Make sure your component exists

In order for auval to run, your component needs to be in one of two locations:

  • ~/Library/Audio/Plug-Ins/Components/
  • /Library/Audio/Plug-Ins/Components/

It cannot be in any other location.

AUv3 can mask AUv2 plugins

If you are building both with JUCE, note that auvaltool -al may only list the AUv3 plugin. A workaround is to delete the .appex to see the AUv2 again.

auvaltool error codes

AudioUnit errors often don't give much to go on, just an error code. If you use JUCE, you won't run into most of the error codes, as JUCE handles things (or errors itself).

However, here's a list of errors and their codes. You can pop an error code into OSStatus.com.

4099

This error code pops up a lot as either Cannot open component: 4099 or FATAL ERROR: Initialize: result: 4099

As Timothy notes on the forum, check your audio/midi inputs/outputs and busses match the type of audio unit you are registering.

Worst case resolutions

Remember, the component registry is a system cache of your plugins. If you are seeing issues persist when they shouldn't (for example, within Logic Pro), do one or more of the following:

  • Remove the plugin completely from the system (look in both ~/Library and/Library variants of the components folder) and then run auvaltool again.
  • Log out and back in again or run killall -9 AudioComponentRegistrar
  • In the worst case, change your plugin name/manufacturer.

Start selling through Moonbase

Sign up today to get started selling your software through Moonbase.

Debugging your Audio Unit plugin with auvaltool (aka auval)