Skip to content

WebSocket: Assert Failure and Extra Duplicate on Multiple Responses #1107

@Argentoz

Description

@Argentoz

I'm encountering a crash in the WebSocket server when sending multiple responses (>1) to a single incoming message in the onmessage handler. The application asserts in the VS C++ std xstring header, and the client receives an extra duplicate of the first server response.

Specifically:

  • Crash on assert in xstring header with "cannot dereference string iterator because the iterator was invalidated (e.g. reallocation occurred, or the string was destroyed)" only in Debug configuration
  • If the server sends N responses (N > 1) in the onmessage handler, the client receives N + 1 responses, where the extra one is a copy of the first server message.
  • Tested on Crow v1.2.1.2 — everything works normally: no crash, no duplicate messages
Image

This happens consistently in Debug builds; single responses work fine.

Environment

  • Crow version: 1.3.0 (installed via vcpkg)
  • ASIO version: 1.32.0 (installed via vcpkg)
  • Compiler: MSVC v143 (19.44.35217)
  • OS: Windows 11 25H2
  • Build: Release/Debug (tested in both; crash happens only in Debug due to asserts)

Steps to Reproduce

  1. Set up a basic Crow WebSocket server with a route handler that responds to incoming messages by sending two or more responses in the onmessage handler.
  2. Connect a WebSocket client (e.g., using a browser or tool like Postman/WebSocket tester).
  3. Send a single message from the client to the server.
  4. Observe the crash: The application will assert and terminate in std::string operations
  5. Additionally, the client receives one extra response that duplicates the first server message.

Simple server code that triggers the issue

#include <iostream>
#include <crow.h>

int main() {
    crow::Crow app;

    CROW_WEBSOCKET_ROUTE(app, "/")
            .onopen([](crow::websocket::connection &/*conn*/) {
                std::cout << "onOpen" << std::endl;
            })
            .onclose([](crow::websocket::connection &/*conn*/, const std::string &reason, uint16_t code) {
                std::cout << "onClose: " << reason << " code: " << code << std::endl;
            })
            .onmessage([](crow::websocket::connection &conn, const std::string &data, bool is_binary) {
                if (is_binary) return;
                std::cout << "onMessage: " << data << std::endl;
                // This triggers the crash when sending multiple times
                conn.send_text("response 1");
                conn.send_text("response 2");
                // If you send only one, it works fine
            });

    app.port(8080).multithreaded().run();
    return 0;
}

More info

Stack

std::_String_const_iterator<std::_String_val<std::_Simple_types<char> > >::operator*() xstring:49
std::_String_iterator<std::_String_val<std::_Simple_types<char> > >::operator*() xstring:281
asio::detail::buffer_debug_check<std::_String_iterator<std::_String_val<std::_Simple_types<char> > > >::operator()() buffer.hpp:658
std::invoke<asio::detail::buffer_debug_check<std::_String_iterator<std::_String_val<std::_Simple_types<char> > > > &>(asio::detail::buffer_debug_check<std::_String_iterator<std::_String_val<std::_Simple_types<char> > > > &) type_traits:1670
std::_Func_impl_no_alloc<asio::detail::buffer_debug_check<std::_String_iterator<std::_String_val<std::_Simple_types<char> > > >, void>::_Do_call() functional:880
std::_Func_class<void>::operator()() functional:926
asio::const_buffer::data() buffer.hpp:259
asio::detail::buffer_sequence_adapter<asio::const_buffer, asio::detail::prepared_buffers<asio::const_buffer, 64> >::validate<const asio::const_buffer *>(const asio::const_buffer *, const asio::const_buffer *) buffer_sequence_adapter.hpp:206
asio::detail::buffer_sequence_adapter<asio::const_buffer, asio::detail::prepared_buffers<asio::const_buffer, 64> >::validate(const asio::detail::prepared_buffers<asio::const_buffer, 64> &) buffer_sequence_adapter.hpp:153
asio::detail::win_iocp_socket_send_op<asio::detail::prepared_buffers<asio::const_buffer,64>,asio::detail::write_op<asio::basic_stream_socket<asio::ip::tcp,asio::any_io_executor>,std::vector<asio::const_buffer,std::allocator<asio::const_buffer> >,std::_Vector_const_iterator<std::_Vector_val<std::_Simple_types<asio::const_buffer> > >,asio::detail::transfer_all_t,`crow::websocket::Connection<crow::SocketAdaptor,crow::Crow<> >::do_write'::`2'::<lambda_1> >,asio::any_io_executor>::do_complete(void *,asio::detail::win_iocp_operation *,const std::error_code &,unsigned long long) win_iocp_socket_send_op.hpp:77
asio::detail::win_iocp_operation::complete(void *, const std::error_code &, unsigned long long) win_iocp_operation.hpp:46
asio::detail::win_iocp_io_context::do_one(unsigned long, asio::detail::win_iocp_thread_info &, std::error_code &) win_iocp_io_context.ipp:474
asio::detail::win_iocp_io_context::run(std::error_code &) win_iocp_io_context.ipp:205
asio::io_context::run() io_context.ipp:63
`crow::Server<crow::Crow<>,crow::TCPAcceptor,crow::SocketAdaptor>::run'::`9'::<lambda_1>::operator()() http_server.h:180
std::invoke<`crow::Server<crow::Crow<>,crow::TCPAcceptor,crow::SocketAdaptor>::run'::`9'::<lambda_1> >(<lambda_1> &&) type_traits:1670
std::_Invoke_stored_explicit<`crow::Server<crow::Crow<>,crow::TCPAcceptor,crow::SocketAdaptor>::run'::`9'::<lambda_1>,0>(std::tuple<`crow::Server<crow::Crow<>,crow::TCPAcceptor,crow::SocketAdaptor>::run'::`9'::<lambda_1> > &&,integer_sequence<unsigned __int64,0>) future:1310
std::_Invoke_stored<`crow::Server<crow::Crow<>,crow::TCPAcceptor,crow::SocketAdaptor>::run'::`9'::<lambda_1> >(std::tuple<`crow::Server<crow::Crow<>,crow::TCPAcceptor,crow::SocketAdaptor>::run'::`9'::<lambda_1> > &&) future:1315
std::_Fake_no_copy_callable_adapter<`crow::Server<crow::Crow<>,crow::TCPAcceptor,crow::SocketAdaptor>::run'::`9'::<lambda_1> >::operator()() future:1342
std::invoke<std::_Fake_no_copy_callable_adapter<`crow::Server<crow::Crow<>,crow::TCPAcceptor,crow::SocketAdaptor>::run'::`9'::<lambda_1> > &>(std::_Fake_no_copy_callable_adapter<`crow::Server<crow::Crow<>,crow::TCPAcceptor,crow::SocketAdaptor>::run'::`9'::<lambda_1> > &) type_traits:1670
std::_Func_impl_no_alloc<std::_Fake_no_copy_callable_adapter<`crow::Server<crow::Crow<>,crow::TCPAcceptor,crow::SocketAdaptor>::run'::`9'::<lambda_1> >,void>::_Do_call() functional:880
std::_Func_class<void>::operator()() functional:926
std::_Packaged_state<void __cdecl(void)>::_Call_immediate() future:533
`std::_Task_async_state<void>::_Task_async_state<void><std::_Fake_no_copy_callable_adapter<`crow::Server<crow::Crow<>,crow::TCPAcceptor,crow::SocketAdaptor>::run'::`9'::<lambda_1> > >'::`2'::<lambda_1>::operator()() future:622
std::invoke<`std::_Task_async_state<void>::_Task_async_state<void><std::_Fake_no_copy_callable_adapter<`crow::Server<crow::Crow<>,crow::TCPAcceptor,crow::SocketAdaptor>::run'::`9'::<lambda_1> > >'::`2'::<lambda_1> &>(std::_Task_async_state<void>::<lambda_1> &) type_traits:1670
std::_Func_impl_no_alloc<`std::_Task_async_state<void>::_Task_async_state<void><std::_Fake_no_copy_callable_adapter<`crow::Server<crow::Crow<>,crow::TCPAcceptor,crow::SocketAdaptor>::run'::`9'::<lambda_1> > >'::`2'::<lambda_1>,void>::_Do_call() functional:880
std::_Func_class<void>::operator()() functional:926
`Concurrency::details::_MakeVoidToUnitFunc'::`2'::<lambda_1>::operator()() ppltasks.h:2363
std::invoke<`Concurrency::details::_MakeVoidToUnitFunc'::`2'::<lambda_1> &>(<lambda_1> &) type_traits:1670
std::_Func_impl_no_alloc<`Concurrency::details::_MakeVoidToUnitFunc'::`2'::<lambda_1>,unsigned char>::_Do_call() functional:882
std::_Func_class<unsigned char>::operator()() functional:926
Concurrency::task<unsigned char>::_InitialTaskHandle<void,`std::_Task_async_state<void>::_Task_async_state<void><std::_Fake_no_copy_callable_adapter<`crow::Server<crow::Crow<>,crow::TCPAcceptor,crow::SocketAdaptor>::run'::`9'::<lambda_1> > >'::`2'::<lambda_1>,Concurrency::details::_TypeSelectorNoAsync>::_LogWorkItemAndInvokeUserLambda<std::function<unsigned char __cdecl(void)> >(function<unsigned char __cdecl(void)>) ppltasks.h:3528
Concurrency::task<unsigned char>::_InitialTaskHandle<void,`std::_Task_async_state<void>::_Task_async_state<void><std::_Fake_no_copy_callable_adapter<`crow::Server<crow::Crow<>,crow::TCPAcceptor,crow::SocketAdaptor>::run'::`9'::<lambda_1> > >'::`2'::<lambda_1>,Concurrency::details::_TypeSelectorNoAsync>::_Init(_TypeSelectorNoAsync) ppltasks.h:3548
Concurrency::task<unsigned char>::_InitialTaskHandle<void,`std::_Task_async_state<void>::_Task_async_state<void><std::_Fake_no_copy_callable_adapter<`crow::Server<crow::Crow<>,crow::TCPAcceptor,crow::SocketAdaptor>::run'::`9'::<lambda_1> > >'::`2'::<lambda_1>,Concurrency::details::_TypeSelectorNoAsync>::_Perform() ppltasks.h:3533
Concurrency::details::_PPLTaskHandle<unsigned char,Concurrency::task<unsigned char>::_InitialTaskHandle<void,`std::_Task_async_state<void>::_Task_async_state<void><std::_Fake_no_copy_callable_adapter<`crow::Server<crow::Crow<>,crow::TCPAcceptor,crow::SocketAdaptor>::run'::`9'::<lambda_1> > >'::`2'::<lambda_1>,Concurrency::details::_TypeSelectorNoAsync>,Concurrency::details::_TaskProcHandle>::invoke() ppltasks.h:1475
Concurrency::details::_TaskProcHandle::_RunChoreBridge(void *) pplwin.h:171
Concurrency::details::_DefaultPPLTaskScheduler::_PPLTaskChore::_Callback(void *) pplwin.h:57

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions