[FATAL BUG]: Transit 2.2 Crashes Ableton when Bouncing Track

Hi there!

As title suggests, Transit 2.1 AU as well as Transit 2.2 crashes my Ableton Live when bouncing a track to audio with Transit on it, it happens very often, I’d say there’s 50 percent chance it will crash Ableton, which makes it really frustrating to work with. I looked into Ableton crash logs with Claude and that’s the report I can provide. I really love Transit 2 and I’d be very grateful if the issue will be fixed soon, because it makes workflow pretty unstable.

Summary

Transit 2 reliably (but intermittently) crashes the host with a null-pointer dereference when I flatten(bouncing to audio) a track that has Transit 2 on it. Flatten triggers an offline freeze/render pass, and the crash happens inside Transit 2’s own parameter-initialization code that runs on the message thread during that pass. It is not tied to playback or to turning knobs.

Environment

  • Plugin: Transit 2 (Audio Unit / .component), version 2.2
    • Path: /Library/Audio/Plug-Ins/Components/Transit 2.component
    • Architecture: arm64
  • Host: Ableton Live 12.3.2 (build 2025-12-15_bba1e05a87)
  • OS: macOS 15.3.1 (build 24D70)
  • Machine: Macbook M4 Pro, Apple Silicon (Mac16,8), ARM-64

What I do

  • Track has a Transit 2 instance.
  • I use Flatten (Ableton’s Flatten = bounce to audio = offline freeze render, then replace track with audio).
  • It crashes Ableton sometimes during the flatten/freeze. Not every time — same project, same track, sometimes it works, sometimes it crashes. No reproducible user-side pattern.
  • Real-time playback is fine; only the offline render (flatten/freeze) crashes.

Crash classification

  • Exception: EXC_BAD_ACCESS (SIGSEGV)
  • Subtype: KERN_INVALID_ADDRESS at 0x0000000000000000 (null-pointer dereference)
  • Faulting thread: main / message thread (com.apple.main-thread)
  • Observed across 6 separate crash reports — all identical signature.

Crashing backtrace (top frames, all in Transit 2)

0  Transit 2  + 0x20f1c0  juce::CharacterFunctions::compare<CharPointer_UTF8, CharPointer_UTF8>(...)

1 Transit 2 + 0x6ed34 baby::ParameterMap::getParameter(juce::String const&) const

2 Transit 2 + 0x18eb10 Transit::addCustomParameters()::$_71 lambda (std::function invocation)

3 Transit 2 + 0x26856c juce::MessageQueue::runLoopSourceCallback(void*)

4 CoreFoundation __CFRUNLOOP_IS_CALLING_OUT_TO_A_SOURCE0_PERFORM_FUNCTION__

... CFRunLoop / NSApplication run (normal host event loop)

One of the six crashes (otherwise identical chain) faults slightly differently at the top — locking a mutex on what looks like a freed parameter object:

0  libsystem_pthread.dylib  pthread_mutex_lock

1 Transit 2 juce::AudioProcessorParameter::beginChangeGesture()

2 Transit 2 Transit::addCustomParameters()::$_71 lambda

3 Transit 2 juce::MessageQueue::runLoopSourceCallback(void*)

Facts (read directly from the crash reports)

  • The crash is a null-pointer dereference (EXC_BAD_ACCESS, address 0x0).
  • It occurs inside Transit 2’s own code, on the message thread, in this chain: Transit::addCustomParameters() lambda (dispatched via juce::MessageQueue::runLoopSourceCallback) → baby::ParameterMap::getParameter(...)juce::CharacterFunctions::compare(...). These symbol names come straight from the Transit 2 binary.
  • One variant faults at beginChangeGesture()pthread_mutex_lock instead, same lambda/dispatch chain.
  • All 6 crash reports share this identical signature.
  • It only happens during Flatten / offline render, not during real-time playback, and not deterministically — the same project/preset sometimes flattens fine and sometimes crashes.

My best guess at the cause (inference — not stated by the crash file; for you to confirm)

The stack suggests Transit::addCustomParameters() posts a lambda to the JUCE message queue to run asynchronously, and when it fires it looks up a parameter in baby::ParameterMap whose backing pointer is null (or, in the variant, a parameter object that has already been freed).

A plausible explanation that fits the evidence is a lifetime/timing race during offline render: when Flatten re-prepares / re-instantiates the plugin and its parameter map, the deferred addCustomParameters callback may run before the map is populated or after it’s torn down. This would also explain the intermittency — identical input crashing only some of the time points to timing, not to a bad preset or bad saved state (a corrupt preset would crash every time).

I can’t confirm the exact mechanism from outside the binary — that part is inference. You’ll be able to verify it against your source.

Workaround I’m using

Bouncing in real time (resample to a new audio track, record-armed, play through) never triggers it — only the offline freeze/flatten render does.

Thanks a lot and I hope hope that this issue will be resolved!
Best regards,
Daniil

NEW! PATTERN WHEN IT HAPPENS


Transit 2 crashes the host with an invalid-memory access every time I flatten a track that was created by duplicating another track that has Transit 2 on it. The crash is inside Transit 2’s own parameter-initialization code, fired from the message queue during the offline (flatten) render. A freshly created Transit 2 instance flattens fine — only a duplicated instance crashes — which points at the duplicate / copy-state code path leaving the parameter map uninitialized.

Steps to reproduce (crashes every time)

  1. Create an audio or MIDI track and add Transit 2 to it (any preset).
  2. Duplicate that track.
  3. Flatten (bounce to audio) the duplicated track → Ableton crashes, 100% of the time.

Flattening the original (non-duplicated) track does not reliably crash. The duplication step is the key precondition.

  • The crash is a null-pointer dereference (EXC_BAD_ACCESS, address 0x0).
  • It occurs inside Transit 2’s own code, on the message thread, in this chain: Transit::addCustomParameters() lambda (dispatched via juce::MessageQueue::runLoopSourceCallback) → baby::ParameterMap::getParameter(...)juce::CharacterFunctions::compare(...). These symbol names come straight from the Transit 2 binary.
  • One variant faults at beginChangeGesture()pthread_mutex_lock instead, same lambda/dispatch chain.
  • All 6 crash reports share this identical signature.
  • It only happens during Flatten / offline render, not during real-time playback.
  • It is deterministic given a duplicated track (the repro above crashes every time).

My best guess at the cause (inference — not stated by the crash file; for you to confirm)

Transit::addCustomParameters() posts a lambda to the JUCE message queue to run later. When it fires it looks up a parameter in baby::ParameterMap, and the backing pointer is null (or, in the variant, a parameter object that has already been freed) → invalid access.

The deterministic “duplicate → flatten” repro suggests the bug is in the instance duplication / copy-state path, not a timing race. When the track is duplicated, the cloned Transit 2 instance appears to schedule addCustomParameters without its ParameterMap being properly initialized (unlike a freshly instantiated plugin). The stale lambda then sits on the message queue and does no harm during normal playback, but the Flatten offline render pumps the message queue, the lambda fires against the uninitialized/null map, and it crashes — every time.

Some more information about null pointer dereference:

It is not always exactly 0x0. Five of the six crashed at 0x0; one (Live-2026-06-19-221437.ips) crashed at 0x18 (= 24 bytes):

So, to be precise:

The crash is an EXC_BAD_ACCESS (SIGSEGV) — an invalid memory access. The faulting address is 0x0 in 5 of 6 reports and 0x18 in the 6th, which is consistent with dereferencing a null (or near-null) pointer.

Thanks for sharing all of this information. I’ll make sure the devs see this so they can patch it!