@ustk Same here, but I don't do UI interaction loops with Claude much so it hasn't affected me.
Post a bug on GitHub.
My Claude says:
+1, and confirmed on current develop (ca472e27b, 2026-08-19): the pattern ustk describes is still there at all three sites. For what it's worth the code is from February (30c732d9b, "added Rest API endpoint /screenshot"), so I don't think the recent commits introduced it, they just make the message thread busier so the 1 s timeout trips more often.
One note on the suggested fixes: switching to SafeAsyncCall::callAsyncAndWait on its own isn't enough. It heap-allocates the WaitableEvent (so the signal() on a dead event goes away), but the lambda still captures capturedImage and captureSuccess by reference, so a late-running lambda still writes into the handler's dead stack frame. The state the lambda writes to has to outlive the handler too. A shared_ptr to a small result struct, captured by value, does it:
struct ScreenshotCapture
{
Image image;
bool success = false;
WaitableEvent done;
};
auto capture = std::make_shared<ScreenshotCapture>();
auto sp = dynamic_cast<ProcessorWithScriptingContent*>(jp);
SafeAsyncCall::callAsyncIfNotOnMessageThread<ProcessorWithScriptingContent>(*sp,
[capture, fullBounds, cropBounds, scale](ProcessorWithScriptingContent& spp)
{
hise::ScriptContentComponent component(&spp);
component.setBounds(fullBounds);
capture->image = component.createComponentSnapshot(cropBounds, true, scale);
capture->success = capture->image.isValid();
capture->done.signal();
});
if (!capture->done.wait(1000))
return req->fail(500, "screenshot capture timed out");
if (!capture->success)
return req->fail(500, "failed to capture screenshot");
auto capturedImage = capture->image;
If the wait times out, the lambda still runs later and writes into the heap struct, which is kept alive by its own copy of the shared_ptr. The handler has already returned its 500 and nothing points at the stack any more. (If the processor was deleted in the meantime, SafeAsyncCaller already skips the call via the WeakReference, so the event is never signalled, which is fine because the waiter has already given up.)
Same treatment for the other two:
handleTestingInteraction (~line 2725): testResult + executed + completed into a shared struct, capture by value.
/api/dsp/screenshot (~line 6508): same as the screenshot site. Note this one also calls mm->runDispatchLoopUntil(500) inside the lambda, so it spends a third of its 1500 ms budget by design; might be worth bumping that timeout while you're in there.