Stream Deck Error 15

I have BTT running on two MacBook Pro M2 Pro laptops. One with a MK.2, the other a Neo. For both I must run regularly Stream Deck plug-ins also, so I have BTT as “Stream Deck Plugin”.

My “home” laptop works great with this. Created Stream Deck triggers, gave them Fixed Identifiers, installed (via BTT) the Stream Deck Plugin, verify presence on Stream deck, create a BTT Action, associate that Fixed Identifier, just works.

On the other laptop, I’ve been trying, as far as I can tell, exactly the same thing for over 90 minutes. Uninstalled and reinstalled plug-ins. Rebooted the computer. What I’m seeing is I often get an exclamation point warning on the deck and a sequence like this in the Stream Deck logs:

2025-10-28T06:29:36.439-07:00  10781-71215  ESDCustomPluginClient         inf void ESDCustomPluginClient::enterLaunching()       Starting 'com.folivora.btt'
2025-10-28T06:30:06.425-07:00  10781-71215  ESDNativeRuntime              war void ESDNativeRuntime::onNativeProcessFinished(int The plugin 'com.folivora.btt' crashed with code 15

That looks like a time-out, since it’s consistent at 30 seconds.

There is other software on the laptop not working, but both BTT and Stream Deck have otherwise been flawless. I can call BTT from the Stream Deck using execute_assigned_actions_for_trigger from osascript, but that’s laggy.

What should i check next?

@Andreas_Hegenberg (Three days later with no response…) Any ideas? Is there some other support channel I should try?

unfortunately I haven't seen that before. Which version of macOS are you running?

Sequoia 15.6.1.

Is there a curl or equivalent I could use to test connectivity to the plug-in?

streamdeck uses websockets to communicate with plugins, but I don't think you can test the communication with other tools.

I can't really think of a reason why this would not work. You mentioned other things have been broken on that machine, do you have an example?

I spent the day doing a bunch of debugging, comparing the working to non-working systems.

They’re listening on different ports and with different names, which seems odd.

The functional system:

lsof -nP -iTCP -sTCP:LISTEN
BTTStream 71391 tony   76u  IPv4 0x7d4be79119e3c666      0t0  TCP 127.0.0.1:23654 (LISTEN)

The non-functional, same command:

BetterTou 3647 wamcna1108    9u  IPv4 0xa09413a0087787a9      0t0  TCP 127.0.0.1:63397 (LISTEN)

No BTTStream on the non-functional, no BetterTou on the functional! But also the listener on the failing one doesn’t change regardless of BTT having Stream Deck configured.

And the API endpoints seem present in the first, not present (returns 404) for /streamdeck and for /api.

Even the plists look pretty much the same, although of course they have different Stream Deck devices listed.

The folders for the app (/Library/Application Support/BetterTouchTool/) are similar except the functional one has more stores and has sqlite files, but from the dates, I suspect that’s just because it’s been around longer. And the ~/Library/Application Support/com.elgato.StreamDeck/Plugins/com.folivora.btt.sdPlugin folders match.

So I’m really interested in why BTTStream is on the functional one listening and BetterTou on the non-functional - but without the endpoints.

I have cleaned and reinstalled the plug-in via BTT several times and rebooted multiple times.

@Andreas_Hegenberg Is there a source repo I could grab the extension (just the extension) code from, so I can debug this further myself?

No, but you could check the logs in ~/Library/Application Support/BetterTouchTool/Logs

They should contain a line starting with "stream deck registration", from your description it kind of sounds like the plugin is never loaded on the stream deck side - in which case that line would be missing.

Right, that line is missing. But how do I address it?

When I reinstall the BTT driver and update the StreamDeck config, I get lines like:

2025/11/13 17:35:16:426|SHORTCUT|SHORTCUT FETCH DISABLE 1|
2025/11/13 17:35:16:533|ASL|app: com.elgato.StreamDeck|
2025/11/13 17:35:43:896|LIC|ADD REGISTER MENU ITEM|
2025/11/13 17:35:43:924|ASL|app: com.hegenberg.BetterTouchTool|
2025/11/13 17:35:46:431|FORM|SETTING LEVEL 1 TRIGGER|
2025/11/13 17:35:46:431|FORM|4 UUID 774451DF-FE9F-42F2-BD54-1477A72E8167 == (null) editing 0 == 0|
2025/11/13 17:35:46:517|NEWUI|Loading Config Debounced|
2025/11/13 17:35:46:517|FORM|1UUID 774451DF-FE9F-42F2-BD54-1477A72E8167 == 774451DF-FE9F-42F2-BD54-1477A72E8167 editing 0 == 0|
2025/11/13 17:35:46:703|ASL|did rebuild form|
2025/11/13 17:35:50:572|ASL|save|
2025/11/13 17:35:50:572|SHORTCUT|SHORTCUT FETCH DISABLE 1|
2025/11/13 17:35:50:633|ASL|app: com.elgato.StreamDeck|
2025/11/13 17:35:50:634|SHORTCUT|SHORTCUT FETCH DISABLE 1|
2025/11/13 17:36:00:014|ASL|HTTPServer: Started HTTP server on port 63397|

But no entries at all in that log when pressing that button, getting the warning symbol.

StreamDeck’s editor considers BTT registered. It shows it’s failing to communicate via the warning triangle. What might cause it to fail on this system but not on another?

@Andreas_Hegenberg Any suggestion on debugging why the plug in is loaded on the one system, but not on the other, even though the Stream Deck Configuration knows about it and can read the config?

unfortunately I also have no clue, I'll do some tests with my different machines to see whether it reproduces anywhere. Is this maybe related to a specific version of the stream deck software?

Both systems are running 7.0.3 of the Stream Deck software. Both report 1.0.0 of BetterTouchTool plug-in. I can’t find a difference.

@Andreas_Hegenberg I know you must be getting annoyed by all this. :smiling_face_with_sunglasses:

I’ve written a substitute but in the process been studying the BTTStreamDeck plug in more.

On both systems, the binary receives the same (roughly) nine parameters and starts up fine and establish a listening socket on port 28196, as specified by the Stream Deck caller, but on the failing system, that’s it. The binary instance close within a few minutes and there’s no other data about the Stream Deck.

On the working system, we get:

2025/11/25 07:49:24:581|ASL|stream deck registration {
    info = "{\"application\":{\"font\":\".AppleSystemUIFont\",\"language\":\"en\",\"platform\":\"mac\",\"platformVersion\":\"15.6.1\",\"version\":\"7.0.3.22071\"},\"colors\":{\"buttonPressedBackgroundColor\":\"#303030FF\",\"buttonPressedBorderColor\":\"#646464FF\",\"buttonPressedTextColor\":\"#969696FF\",\"disabledColor\":\"#8C8C8C7F\",\"highlightColor\":\"#8C8C8CFF\",\"mouseDownColor\":\"#BABABAFF\"},\"devicePixelRatio\":2,\"devices\":[{\"id\":\"d4e5e0dc4a2997f4bdbae157a5ffb71e\",\"name\":\"MK.2\",\"size\":{\"columns\":5,\"rows\":3},\"type\":0},{\"id\":\"9d69d9888a7460fc0cae6c47f6b371c1\",\"name\":\"Stream Deck Neo\",\"size\":{\"columns\":4,\"rows\":2},\"type\":9}],\"plugin\":{\"uuid\":\"com.folivora.btt\",\"version\":\"1.0.0\"}}";
    pluginUUID = f51a035c715aa48eae6e6ee79fe96232;
    port = 28196;
    registerEvent = registerPlugin;
}|
2025/11/25 07:49:24:583|ASL|JSON DIct: {
    application =     {
        font = ".AppleSystemUIFont";
        language = en;
        platform = mac;
        platformVersion = "15.6.1";
        version = "7.0.3.22071";
    };
    colors =     {
        buttonPressedBackgroundColor = "#303030FF";
        buttonPressedBorderColor = "#646464FF";
        buttonPressedTextColor = "#969696FF";
        disabledColor = "#8C8C8C7F";
        highlightColor = "#8C8C8CFF";
        mouseDownColor = "#BABABAFF";
    };
    devicePixelRatio = 2;
    devices =     (
                {
            id = d4e5e0dc4a2997f4bdbae157a5ffb71e;
            name = "MK.2";
            size =             {
                columns = 5;
                rows = 3;
            };
            type = 0;
        },
                {
            id = 9d69d9888a7460fc0cae6c47f6b371c1;
            name = "Stream Deck Neo";
            size =             {
                columns = 4;
                rows = 2;
            };
            type = 9;
        }
    );
    plugin =     {
        uuid = "com.folivora.btt";
        version = "1.0.0";
    };
}|
2025/11/25 07:49:24:583|ASL|register plugin {
    info = "{\"application\":{\"font\":\".AppleSystemUIFont\",\"language\":\"en\",\"platform\":\"mac\",\"platformVersion\":\"15.6.1\",\"version\":\"7.0.3.22071\"},\"colors\":{\"buttonPressedBackgroundColor\":\"#303030FF\",\"buttonPressedBorderColor\":\"#646464FF\",\"buttonPressedTextColor\":\"#969696FF\",\"disabledColor\":\"#8C8C8C7F\",\"highlightColor\":\"#8C8C8CFF\",\"mouseDownColor\":\"#BABABAFF\"},\"devicePixelRatio\":2,\"devices\":[{\"id\":\"d4e5e0dc4a2997f4bdbae157a5ffb71e\",\"name\":\"MK.2\",\"size\":{\"columns\":5,\"rows\":3},\"type\":0},{\"id\":\"9d69d9888a7460fc0cae6c47f6b371c1\",\"name\":\"Stream Deck Neo\",\"size\":{\"columns\":4,\"rows\":2},\"type\":9}],\"plugin\":{\"uuid\":\"com.folivora.btt\",\"version\":\"1.0.0\"}}";
    pluginUUID = f51a035c715aa48eae6e6ee79fe96232;
    port = 28196;
    registerEvent = registerPlugin;
}|
2025/11/25 07:49:24:663|ASL|app: com.elgato.StreamDeck|
2025/11/25 07:49:24:663|SWI|APPS EQUAL|
2025/11/25 07:49:25:246|ASL|streamdeck socket opened|

and the connection lives.

So my guess is the BTTStreamDeck process is crashing on the failing Mac before getting to outputting “|ASL|JSON DIct: {“

Working this theory, I looked at the System Logs for Stream Deck and found:

2025-11-25T07:33:53.457-08:00 95190-96538457 MARKETPLACE inf auto ESDMarketplaceAssetManager::onScheduledCheckF Finished scheduled check for update
2025-11-25T07:34:17.270-08:00 95190-96538457 ESDNativeRuntime war void ESDNativeRuntime::onNativeProcessFinished(int The plugin 'com.folivora.btt' crashed with code 15
2025-11-25T07:34:19.288-08:00 95190-96538457 ESDCustomPluginClient inf void ESDCustomPluginClient::enterLaunching() Starting 'com.folivora.btt'
2025-11-25T07:34:49.269-08:00 95190-96538457 ESDNativeRuntime war void ESDNativeRuntime::onNativeProcessFinished(int The plugin 'com.folivora.btt' crashed with code 15
2025-11-25T07:34:51.274-08:00 95190-96538457 ESDCustomPluginClient inf void ESDCustomPluginClient::enterLaunching() Starting 'com.folivora.btt'
2025-11-25T07:35:21.268-08:00 95190-96538457 ESDNativeRuntime war void ESDNativeRuntime::onNativeProcessFinished(int The plugin 'com.folivora.btt' crashed with code 15
2025-11-25T07:35:23.281-08:00 95190-96538457 ESDCustomPluginClient inf void ESDCustomPluginClient::enterLaunching() Starting 'com.folivora.btt'
2025-11-25T07:35:51.632-08:00 95190-96538457 ESDSleepHelper inf static void ESDSleepHelper::WakeUpAllDevices() Wake up device(s)
2025-11-25T07:35:53.268-08:00 95190-96538457 ESDNativeRuntime war void ESDNativeRuntime::onNativeProcessFinished(int The plugin 'com.folivora.btt' crashed with code 15
2025-11-25T07:35:55.276-08:00 95190-96538457 ESDCustomPluginClient inf void ESDCustomPluginClient::enterLaunching() Starting 'com.folivora.btt'
2025-11-25T07:36:25.268-08:00 95190-96538457 ESDNativeRuntime war void ESDNativeRuntime::onNativeProcessFinished(int The plugin 'com.folivora.btt' crashed with code 15
2025-11-25T07:36:27.282-08:00 95190-96538457 ESDCustomPluginClient inf void ESDCustomPluginClient::enterLaunching() Starting 'com.folivora.btt'
2025-11-25T07:36:57.270-08:00 95190-96538457 ESDNativeRuntime war void ESDNativeRuntime::onNativeProcessFinished(int The plugin 'com.folivora.btt' crashed with code 15
2025-11-25T07:36:59.280-08:00 95190-96538457 ESDCustomPluginClient inf void ESDCustomPluginClient::enterLaunching() Starting 'com.folivora.btt'
2025-11-25T07:37:29.269-08:00 95190-96538457 ESDNativeRuntime war void ESDNativeRuntime::onNativeProcessFinished(int The plugin 'com.folivora.btt' crashed with code 15
2025-11-25T07:37:31.273-08:00 95190-96538457 ESDCustomPluginClient inf void ESDCustomPluginClient::enterLaunching() Starting 'com.folivora.btt'
2025-11-25T07:38:01.268-08:00 95190-96538457 ESDNativeRuntime war void ESDNativeRuntime::onNativeProcessFinished(int The plugin 'com.folivora.btt' crashed with code 15
2025-11-25T07:38:03.280-08:00 95190-96538457 ESDCustomPluginClient inf void ESDCustomPluginClient::enterLaunching() Starting 'com.folivora.btt'
2025-11-25T07:38:33.267-08:00 95190-96538457 ESDNativeRuntime war void ESDNativeRuntime::onNativeProcessFinished(int The plugin 'com.folivora.btt' crashed with code 15
2025-11-25T07:38:35.277-08:00 95190-96538457 ESDCustomPluginClient inf void ESDCustomPluginClient::enterLaunching() Starting 'com.folivora.btt'
2025-11-25T07:39:05.269-08:00 95190-96538457 ESDNativeRuntime war void ESDNativeRuntime::onNativeProcessFinished(int The plugin 'com.folivora.btt' crashed with code 15
2025-11-25T07:39:07.287-08:00 95190-96538457 ESDCustomPluginClient inf void ESDCustomPluginClient::enterLaunching() Starting 'com.folivora.btt'
2025-11-25T07:39:37.272-08:00 95190-96538457 ESDNativeRuntime war void ESDNativeRuntime::onNativeProcessFinished(int The plugin 'com.folivora.btt' crashed with code 15
2025-11-25T07:39:39.283-08:00 95190-96538457 ESDCustomPluginClient inf void ESDCustomPluginClient::enterLaunching() Starting 'com.folivora.btt'
2025-11-25T07:40:09.268-08:00 95190-96538457 ESDNativeRuntime war void ESDNativeRuntime::onNativeProcessFinished(int The plugin 'com.folivora.btt' crashed with code 15
2025-11-25T07:40:09.269-08:00 95190-96538457 SDK inf void ESDCustomPlugin::onSessionEnded() Plugin 'com.folivora.btt' finished
2025-11-25T07:40:09.269-08:00 95190-96538457 ESDCustomPluginClient war void ESDCustomPluginClient::enterDisabled() Plugin 'com.folivora.btt' is unstable an was disabled.

I wrote a quick websocket tester; BTTStreamDeck is not creating the correct output socket so there's not a lot more I can test without more insight into the code.Any ideas?

If you have a substitute you can try fowarding the registration information from that to BTT:

NSDictionary *streamDeckRegistration = @{ @"port": @(port), @"pluginUUID": pluginUUID, @"registerEvent": registerEvent,@"info": info };
        
[[NSDistributedNotificationCenter defaultCenter] postNotificationName:@"BTTStreamDeckRegistration" object:nil userInfo:streamDeckRegistration deliverImmediately:YES];

There is basically nothing in the BTTStreamDeck process that could crash (unless the stream deck process provides some invalid data to the plugin). I guess exit code 15 is when the plugin is for some reason closed by the Stream Deck software, but I have no idea why. The only purpose of the stream deck plugin is to forward the registration info to BTT.