AU wrapper: SetBusCount syncs the layout tag of the wrong bus, Initialize then fails with kAudioUnitErr_FormatNotSupported

JUCE 9.0.1, macOS 26, Logic Pro 12.3.1 (hosts AUv2 out of process). Effect plugin with canAddBus() returning true.

Logic adds a side-chain input bus to every effect whose bus count is writable: kAudioUnitProperty_ElementCount (input) 1 → 2, then sets stereo stream formats and calls AudioUnitInitialize. Initialize fails with -10868 and Logic shows the plugin with its generic view, no editor and no sound.

Cause, juce_audio_plugin_client_AU_1.mm, JuceAU::SetBusCount:

for (busNr = (busCount - 1); busNr != (requestedNumBus - 1); busNr += (requestedNumBus > busCount ? 1 : -1))
{
    if (requestedNumBus > busCount)
    {
        if (! juceFilter->addBus (isInput))
            break;

        err = syncAudioUnitWithChannelSet (isInput, busNr,
                                           juceFilter->getBus (isInput, busNr + 1)->getDefaultLayout());

busNr is the index of the last bus before the addition, so the bus just added is busNr + 1 - the line already reads its layout from there but syncs element busNr: the old bus gets the new bus’s layout, the new bus keeps its resize()d tag of 0 and the AUSDK default stream format. syncProcessorWithAudioUnit() in Initialize() then compares element busNr + 1’s channel count with a tag of 0 channels and returns kAudioUnitErr_FormatNotSupported.

Reproduced outside Logic with a 40-line host: AudioComponentInstantiate out of process, AudioUnitSetProperty (ElementCount, input, 2), stereo formats on all elements, AudioUnitInitialize → -10868; with the fix → noErr, for 2 and 3 input busses.

Fix (patch attached): sync busNr + 1. A cleaner form would iterate over the index of the new bus itself:

for (int bus = busCount; bus < requestedNumBus; ++bus)
{
    if (! juceFilter->addBus (isInput)) break;
    err = syncAudioUnitWithChannelSet (isInput, bus, juceFilter->getBus (isInput, bus)->getDefaultLayout());

The patch against 9.0.1:

diff --git a/modules/juce_audio_plugin_client/juce_audio_plugin_client_AU_1.mm b/modules/juce_audio_plugin_client/juce_audio_plugin_client_AU_1.mm
index 579e261816..72fcce20de 100644
--- a/modules/juce_audio_plugin_client/juce_audio_plugin_client_AU_1.mm
+++ b/modules/juce_audio_plugin_client/juce_audio_plugin_client_AU_1.mm
@@ -347,7 +347,7 @@ OSStatus SetBusCount (AudioUnitScope scope, UInt32 count) override
                         if (! juceFilter->addBus (isInput))
                             break;
 
-                        err = syncAudioUnitWithChannelSet (isInput, busNr,
+                        err = syncAudioUnitWithChannelSet (isInput, busNr + 1,
                                                            juceFilter->getBus (isInput, busNr + 1)->getDefaultLayout());
                         if (err != noErr)
                             break;