WebSocket and HTTP fails silently

Straight to the code, HTTP and WebSocket in Julia can’t establish a connection. It seem to be a general error with Julia or the package.

I’m using the latest versions on mac.
julia version 1.11.4

Minimal Example using WebSocket Package:

using WebSockets, Logging

# Set up logging to print info messages
global_logger(ConsoleLogger(stdout, Logging.Info))

@info "Starting WebSocket test"
WebSockets.open("wss://echo.websocket.org") do ws
    @info "Connected"
    io = ws.io
    write(io, "Hello!")
    while isopen(io)
        @info "Received: $(readline(io))"
    end
end

Error:
It’s just getting stuck until I het CTRL+C

❯ julia test_websocket.jl
[ Info: Starting WebSocket test
^C
[42245] signal 2: Interrupt: 2
in expression starting at /Users/admin/julia/test_websocket.jl:7
kevent at /usr/lib/system/libsystem_kernel.dylib (unknown line)
unknown function (ip: 0x0)
Allocations: 5944328 (Pool: 5944228; Big: 100); GC: 7
val already in a list
atexit hook threw an error: ErrorException("schedule: Task not runnable")
error at ./error.jl:35
#schedule#761 at ./task.jl:884
schedule at ./task.jl:876 [inlined]
uv_writecb_task at ./stream.jl:1200
jfptr_uv_writecb_task_67322.1 at /Applications/Julia-1.11.app/Contents/Resources/julia/lib/julia/sys.dylib (unknown line)
jlcapi_uv_writecb_task_67793.1 at /Applications/Julia-1.11.app/Contents/Resources/julia/lib/julia/sys.dylib (unknown line)
uv__write_callbacks at /workspace/srcdir/libuv/src/unix/stream.c:926
uv__stream_io at /workspace/srcdir/libuv/src/unix/stream.c:1227
uv__run_pending at /workspace/srcdir/libuv/src/unix/core.c:824
uv_run at /workspace/srcdir/libuv/src/unix/core.c:420
ijl_task_get_next at /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-honeycrisp-XG3Q6T6R70.0/build/default-honeycrisp-XG3Q6T6R70-0/julialang/julia-release-1-dot-11/src/scheduler.c:522
poptask at ./task.jl:1012
wait at ./task.jl:1021
uv_write at ./stream.jl:1081
unsafe_write at ./stream.jl:1154
write at ./strings/io.jl:248 [inlined]
print at ./strings/io.jl:250
unknown function (ip: 0x115324053)
showerror at ./errorshow.jl:152
unknown function (ip: 0x11531c073)
_atexit at ./initdefs.jl:462
jfptr__atexit_69886.1 at /Applications/Julia-1.11.app/Contents/Resources/julia/lib/julia/sys.dylib (unknown line)
jl_apply at /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-honeycrisp-XG3Q6T6R70.0/build/default-honeycrisp-XG3Q6T6R70-0/julialang/julia-release-1-dot-11/src/./julia.h:2157 [inlined]
ijl_atexit_hook at /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-honeycrisp-XG3Q6T6R70.0/build/default-honeycrisp-XG3Q6T6R70-0/julialang/julia-release-1-dot-11/src/init.c:271
jl_exit_thread0_cb at /Users/julia/.julia/scratchspaces/a66863c6-20e8-4ff4-8a62-49f30b1f605e/agent-cache/default-honeycrisp-XG3Q6T6R70.0/build/default-honeycrisp-XG3Q6T6R70-0/julialang/julia-release-1-dot-11/src/./signals-mach.c:518

Minimal Example using HTTP Package:

using HTTP, Logging

# Set up logging to print info messages
global_logger(ConsoleLogger(stdout, Logging.Info))

@info "Starting WebSocket test"
HTTP.WebSockets.open("wss://stream.binance.com:9443/ws/btcusdt@trade") do ws
    @info "Connected to Binance WebSocket"
    try
        while HTTP.WebSockets.isopen(ws)
            msg = HTTP.WebSockets.receive(ws)
            @info "Received message: $msg"
        end
    catch e
        @error "An error occurred: $e"
    end
    @info "WebSocket closed"
end

Error:

❯ julia test_websocket.jl
[ Info: Starting WebSocket test
[ Info: Connected to Binance WebSocket
┌ Error: An error occurred: MethodError(isopen, (HTTP.WebSockets.WebSocket(Base.UUID("449163ad-92d2-48b0-a52a-a498d3627bc8"), ⏸    0s stream.binance.com:9443:56775 RawFD(19), HTTP.Messages.Request:
│ """
│ GET /ws/btcusdt@trade HTTP/1.1
│ Upgrade: websocket
│ Connection: Upgrade
│ Sec-WebSocket-Key: v31V2lXd7MkKDYe029mdzg==
│ Sec-WebSocket-Version: 13
│ Host: stream.binance.com:9443
│ Accept: */*
│ User-Agent: HTTP.jl/1.11.4
│ Accept-Encoding: gzip
│
│ [Message Body was streamed]""", HTTP.Messages.Response:
│ """
│ HTTP/1.1 101 Switching Protocols
│ Date: Tue, 18 Mar 2025 17:35:01 GMT
│ Connection: upgrade
│ Upgrade: websocket
│ Sec-WebSocket-Accept: ux20lIJwCmY9W4zdnv9h/L81RAk=
│
│ """, 9223372036854775807, 1024, true, UInt8[], UInt8[], false, false),), 0x0000000000006854)
└ @ Main ~/Documents/GitHub/Some.Capital/BMM-Algo/bmm_jl/test_websocket.jl:15
[ Info: WebSocket closed

The behavior you’re observing might seem like a runtime issue, but it’s actually how Julia handles OS signals.

Additionally, some of the other problems with these minimal examples are due to incorrect API usage (specifically with the HTTP package):

  • Use send() instead of write() to send data to a WebSocket.
  • Use isopen(ws.io) instead of isopen(ws) to properly check connection status.

If you’re looking to handle Ctrl-C interruptions and signal handling correctly, here’s a minimal example using Visor:

using Visor
using HTTP.WebSockets

mutable struct WS
    ws::Union{Nothing,WebSockets.WebSocket}
    inbox::Channel
    WS(inbox) = new(nothing, inbox)
end

function receive(sock::WS)
    while isopen(sock.ws.io)
        msg = WebSockets.receive(sock.ws)
        put!(sock.inbox, msg)
    end
end

function ws_process(pd)
    sock = WS(pd.inbox)
    count = 1

    @async WebSockets.open("wss://echo.websocket.org") do ws
        sock.ws = ws
        receive(sock)
    end

    for msg in pd.inbox
        if isshutdown(msg)
            @info "Shutting down"
            close(sock.ws)
            break
        end
        @info "Received message: $msg"
        sleep(1)
        WebSockets.send(sock.ws, "hello $count")
        count += 1
    end
end

supervise(process(ws_process))

Disclaimer: I’m the author of Visor, and I created this package to overcome the same issues you’re facing

2 Likes

It’s a fully working solution, is nobody able to run it successfully? Is there a Regression in the HTTP and WebSocket implementation of Julia?

WebSockets.open("wss://echo.websocket.org") do ws
    send(ws, "Hello")
    while !HTTP.WebSockets.isclosed(ws)
        s = receive(ws)
        println(s)
    end
end

This is awesome!! Honored to talk to the author here. I’ve tried it and it works!!
Is using HTTP.Websocket better than WebSockets directly? Seems like two competing libraries.

Whereas the example provided by @sdanisch doesn’t work and has the same issue with failing silently without an error.

I forgot the imports:

using HTTP
using HTTP.WebSockets
WebSockets.open("wss://echo.websocket.org") do ws
    send(ws, "Hello")
    while !HTTP.WebSockets.isclosed(ws)
        s = receive(ws)
        println(s)
    end
end

This is with the latest HTTP version (v1.10.15).
I guess for you it fails silently because you mess with the logging.
For me it prints a wall of errors when forgetting the imports.

HTTP.Websocket better than WebSockets

The WebSockets package is deprecated and HTTP.jl is the way to go.