Deployed streamlit stays in skeleton/loading screen

Hi there, i have a deployed streamlit-app in an openshift-environment where in front of the cluster-namespace F5-BigIPs are used for SSO to authenticate and to use WAF (also from F5) as well as an AV-Scanner (SkyHigh).

When I actually open the app in my browser it stays in the skeleton/loading screen (btw locally it works). In can see in my browser that (of course) the js-stuff is downloaded and then i can see that the app is calling

  • the health-status with the response “ok”
  • then the host-config where the response comes as an json with the allowedOrigins, useExternalAuthToken and enableCustomParentMessages
  • then the favicon.png is downloaded
  • finally the stream / websocket is tried to be established and from this point the whole request-cascade starting again with asking for the health

In the backend-logs i can see this:

2024-08-13 06:59:37.206 Error parsing message with type ‘BackMsg’
12/opt/conda/envs/bordnetz_gpt_webapp/lib/python3.10/site-packages/streamlit/web/server/browser_websocket_handler.py:166: RuntimeWarning: Unexpected end-group tag: Not all data was converted
13 msg.ParseFromString(payload)
142024-08-13 07:12:18.933 No handler for “None”
152024-08-13 07:12:49.202 Error parsing message with type ‘BackMsg’
162024-08-13 07:13:19.490 Error parsing message with type ‘BackMsg’
172024-08-13 07:13:49.798 Error parsing message with type ‘BackMsg’
182024-08-13 07:14:20.071 Error parsing message with type ‘BackMsg’
192024-08-13 07:14:50.349 Error parsing message with type ‘BackMsg’
202024-08-13 07:15:20.633 No handler for “None”
21[libprotobuf ERROR google/protobuf/wire_format_lite.cc:618] String field ‘BackMsg.debug_last_backmsg_id’ contains invalid UTF-8 data when parsing a protocol buffer. Use the ‘bytes’ type if you intend to send raw bytes.
222024-08-13 07:15:50.906 Error parsing message with type ‘BackMsg’
232024-08-13 07:16:21.193 Error parsing message with type ‘BackMsg’
242024-08-13 07:16:51.470 Error parsing message with type ‘BackMsg’
252024-08-13 07:17:21.797 Error parsing message with type ‘BackMsg’
262024-08-13 07:17:52.144 No handler for “None”
272024-08-13 07:18:08.560 Error parsing message with type ‘BackMsg’
282024-08-13 07:18:39.242 No handler for “None”

The deployment runs actually with python 3.10.9 and streamlit 1.33.0.

By now i could already figure out that the AV-Scanner causes the trouble. But for now it is not clear to me why since there is no official blocking message. Even the guys from SkyHigh are for now pretty clueless. Since the deactivation the AV-Scanner is no option and we’re already using other apps with websocket-communication (e.g. Vaadin) i’am wondering what is doing streamlit diffrent to other “websocketeers”? Has somebody an idea what the reason for this behaviour is? And is there an api-spec of streamlit which describes this basic communication between the frontend and the backend?
The SkyHigh-guys also asked if there some special base64-encoded requests/payloads are used in the communication.

So any help/ideas are appreciated! BR

Hey,

I am also interested in anything regarding WebSocket problems, as some of our clients have security settings that block WebSocket connections.

Usually, it’s enough to talk to their IT department, and they can whitelist the URL so that the WebSocket connection can be established.

You can also reproduce the problem by using Firefox and setting the max allowed connections to 0. If you do that it shouldn’t work locally as well.

One more thing:

We wanted a “nicer” error screen than the never-ending skeleton loading animation. Because of that, we inserted some custom JavaScript into the index.html from Streamlit that gets executed at the start.

It basically checks whether it can connect to the WebSocket, and if not, an error message is displayed. Here is the code for testing if the WebSocket connection was successful. You could also paste it into the console and try it out there quickly.

Here ist JS-Code for the checking. I have excluded the part where the shown content is being changed.


function getWebSocketURL() {
            const protocol = window.location.protocol === 'https:' ? 'wss:' : 'ws:';
            const host = window.location.hostname;
            const port = window.location.port;
            // if the port is not none (i.e. not the default port 80 or 443), include it in the URL
            if (port) {
                return `${protocol}//${host}:${port}/_stcore/stream`;
            }
            else {
                return `${protocol}//${host}/_stcore/stream`;
            }
        }

        function checkWebSocketConnection() {
            return new Promise((resolve, reject) => {
                if ("WebSocket" in window) {
                    const wsURL = getWebSocketURL();
                    const ws = new WebSocket(wsURL);

                    ws.onopen = function() {
                        ws.close();
                        resolve(true);
                        console.log("Success");
                    };

                    ws.onerror = function() {
                        displayWarning(wsURL);
                        console.log("Websocket connection failed. URL: " + wsURL);
                        reject(new Error("WebSocket connection failed."));
                    };
                    
                    ws.onclose = function() {
                        console.log("WebSocket connection closed.");
                    };
                } else {
                    //displayWarning(wsURL); //this functions shows our message on the screen
                    reject(new Error("WebSocket not supported."));
                }
            });
        }

        async function main() {
            try {
                await checkWebSocketConnection();
                return
            } catch (error) {
                console.error(error.message);
                return
            }
        }

In the console:

Kind regards
Fabian