Websocket max message size is not respected

Panel serve command does not respect websocket max message size. I have tested the same code with a pandas dataframe that takes 9.2MB memory and panel works fine. However, when I increase the size to 13MB, websocket connection is automatically closed. In both cases, the app was launched with 200MB max allowed message size. See the screenshot. In both cases, the app was launched with the following command
panel serve testing_passing_raw.py --autoreload --port 8501 --allow-websocket-origin=jupyterhub-xxxxxx.com --websocket-max-message-size=209715200

Here’s the output from the screen (sorry I am only allowed to upload one image as a new user).

Can someone please help me understand and solve the issue?

================================================================================
2024-02-01 01:56:28,487 Starting Bokeh server version 3.1.1 (running on Tornado 6.2)
2024-02-01 01:56:28,488 Torndado websocket_max_message_size set to 209715200 bytes (200.00 MB)
2024-02-01 01:56:28,488 User authentication hooks NOT provided (default user enabled)
2024-02-01 01:56:28,490 Bokeh app running at: http://localhost:8501/testing_passing_raw
2024-02-01 01:56:28,490 Starting Bokeh server with process id: 1666
================================================================================
<class 'pandas.core.frame.DataFrame'>
RangeIndex: 600000 entries, 0 to 599999
Data columns (total 3 columns):
 #   Column  Non-Null Count   Dtype
---  ------  --------------   -----
 0   A       600000 non-null  int64
 1   B       600000 non-null  int64
 2   C       600000 non-null  int64
dtypes: int64(3)
memory usage: 13.7 MB

Would you be able to share a minimal example and submit a GitHub issue? Thanks!

Here’s the minimal code that demonstrate this issue (sorry I could not find an edit button for the post):

import panel as pn
import pandas as pd
import numpy as np

pn.extension(sizing_mode="stretch_width")
pn.extension(template="bootstrap")
pn.extension('perspective')
pn.extension('tabulator')

df = pd.DataFrame(np.random.randint(0,100,size=(600000, 3)), columns=list('ABC'))
print('==' * 40)
print(df.info())
print('==' * 40)

######################################
## first tab tab
######################################
gspec = pn.GridSpec(sizing_mode='stretch_both', min_height = 600, max_height=2000)
gspec[:, 0] = pn.Spacer(styles=dict(background='#FFFFFF'))
gspec[:, 8] = pn.Spacer(styles=dict(background='#FFFFFF'))

persepctive_box = pn.pane.Perspective(df)
gspec[0:4, 1:8] = persepctive_box


###################################
## arrange tabs
####################################
tabs = pn.Tabs(('Test Panel', gspec), dynamic = True)

####################################
## serve the app
####################################
tabs.servable("Dataframe size issue demo")

Seems to work for me.

With /without the websocket message

Thanks for trying this out. What is your panel version?
Here’s my pip freeze output:

agate==1.7.0
aiohttp==3.8.3
aiosignal==1.3.1
airbyte-api-client 
alembic==1.8.1
analytics-python==1.4.post1
anyio==3.6.2
appnope==0.1.3
argo-workflows==3.6.1
argon2-cffi==21.3.0
argon2-cffi-bindings==21.2.0
asttokens==2.1.0
async-generator==1.10
async-timeout==4.0.2
attrdict==2.0.1
attrs==22.1.0
Babel==2.11.0
backcall==0.2.0
backoff==1.10.0
backports.zoneinfo==0.2.1
beautifulsoup4==4.6.3
betterproto==1.2.5
bleach==5.0.1
bokeh==3.1.1
cachetools==5.2.0
certifi==2022.9.24
certipy==0.1.3
cffi==1.15.1
charset-normalizer==2.1.1
click==8.1.3
colorama==0.4.6
commonmark==0.9.1
contourpy==1.1.1
cryptography==38.0.3
csscompressor==0.9.5
daiquiri==3.2.1
dbt-core==1.4.6
dbt-extractor==0.4.1
dbt-trino==1.4.2
debugpy==1.6.3
decorator==5.1.1
deepdiff==5.7.0
defusedxml==0.7.1
delegator.py==0.1.1
dictdiffer==0.9.0
distlib==0.3.6
distro==1.8.0
entrypoints==0.4
executing==1.2.0
fastjsonschema==2.16.2
filelock==3.8.0
frozenlist==1.3.3
future==0.18.3
gitdb==4.0.9
GitPython==3.1.29
google-auth==2.14.1
greenlet==2.0.2
grpclib==0.4.5
h2==4.1.0
hologram==0.0.16
hpack==4.0.0
hyperframe==6.0.1
idna==3.4
importlib-metadata==5.0.0
importlib-resources==5.10.0
invectio==0.2.2
ipykernel==6.17.1
ipynbname==2021.3.2
ipython==8.6.0
ipython-genutils==0.2.0
isodate==0.6.1
jedi==0.18.1
Jinja2==3.1.2
json5==0.9.10
jsonformatter==0.3.2
jsonschema==4.17.0
jsonschema-specifications==2023.7.1
jupyter-contrib-core==0.4.2
jupyter-contrib-nbextensions==0.7.0
jupyter-highlight-selected-word==0.2.0
jupyter-kernel-gateway==2.4.0
jupyter-nbextensions-configurator==0.6.3
jupyter-nbutils==0.1.3
jupyter-require==0.6.1
jupyter-server==1.23.2
jupyter-server-mathjax==0.2.6
jupyter-server-proxy==3.2.2
jupyter-telemetry==0.1.0
jupyter_client==7.4.7
jupyter_core==5.0.0
jupyterhub==1.5.0
jupyterlab==3.5.0
jupyterlab-git==0.39.3
jupyterlab-pygments==0.2.2
jupyterlab-requirements==0.16.2
jupyterlab-spellchecker==0.7.2
jupyterlab_server==2.16.3
kubernetes==11.0.0
leather==0.3.4
linkify-it-py==2.0.3
Logbook==1.5.3
lxml==4.9.1
Mako==1.2.4
Markdown==3.5.2
markdown-it-py==3.0.0
MarkupSafe==2.1.1
mashumaro==3.3.1
matplotlib-inline==0.1.6
mdit-py-plugins==0.4.0
mdurl==0.1.2
micropipenv==1.4.4
minimal-snowplow-tracker==0.0.2
mistune==2.0.4
mock==4.0.3
mod-wsgi==4.9.2
monotonic==1.6
msgpack==1.0.5
multidict==6.0.2
nbclassic==0.4.8
nbclient==0.7.0
nbconvert==7.2.5
nbdime==3.1.1
nbformat==5.7.0
nest-asyncio==1.5.6
networkx==2.8.8
notebook==6.4.12
notebook_shim==0.2.2
numpy==1.24.4
oauthlib==3.2.2
octavia-cli 
openshift==0.11.0
ordered-set==4.0.2
packaging==21.3
pamela==1.0.0
pandas==2.0.3
pandocfilters==1.5.0

panel==1.2.3

param==2.0.2
parsedatetime==2.4
parso==0.8.3
pathspec==0.10.3
pexpect==4.8.0
pickleshare==0.7.5
pillow==10.2.0
pkgutil_resolve_name==1.3.10
platformdirs==2.5.4
prometheus-client==0.15.0
prompt-toolkit==3.0.32
psutil==5.9.4
ptyprocess==0.7.0
pure-eval==0.2.2
pyasn1==0.4.8
pyasn1-modules==0.2.8
pycparser==2.21
pyelftools==0.29
Pygments==2.13.0
pyOpenSSL==22.1.0
pyparsing==3.0.9
pyrsistent==0.19.2
python-dateutil==2.8.2
python-json-logger==2.0.4
python-slugify==6.1.2
python-string-utils==1.0.0
pytimeparse==1.1.8
pytz==2022.6
pytz-deprecation-shim==0.1.0.post0
pyviz_comms==3.0.1
PyYAML==6.0
pyzmq==24.0.1
referencing==0.30.2
requests==2.28.1
requests-oauthlib==1.3.1
rfc5424-logging-handler==1.4.3
rich==12.6.0
rich-click==1.5.2
rpds-py==0.9.2
rsa==4.9
ruamel.yaml==0.17.21
ruamel.yaml.clib==0.2.7
semantic-version==2.10.0
Send2Trash==1.8.0
sentry-sdk==1.11.0
simpervisor==0.4
six==1.16.0
smmap==5.0.0
sniffio==1.3.0
SQLAlchemy==1.4.44
sqlparse==0.4.3
stack-data==0.6.1
stringcase==1.2.0
supervisor==4.1.0
termcolor==2.1.0
termcolor-whl==1.1.2
terminado==0.17.0
text-unidecode==1.3
thamos==1.29.1
thoth-analyzer==0.1.8
thoth-common==0.36.6
thoth-python==0.16.11
tinycss2==1.2.1
toml==0.10.2
tomli==2.0.1

tornado==6.2

tqdm==4.66.1
traitlets==5.5.0
trino==0.326.0
typing_extensions==4.4.0
tzdata==2022.6
tzlocal==4.2
uc-micro-py==1.0.2
urllib3==1.26.12
virtualenv==20.16.7
voluptuous==0.13.1
wcwidth==0.2.5
webencodings==0.5.1
websocket-client==1.4.2
Werkzeug==2.3.6
xyzservices==2023.10.1
yarl==1.8.1
yaspin==2.2.0
zipp==3.10.0

Is this running on a JupyterHub deployment? Is it possible that JupyterHub is proxying the WS requests and imposing its own limit?

It is running on JupyterHub deployment. I tested the same code on my local after @ahuang11 kindly responded that it worked on his end, and it is working fine on my local too. You’re probably right in saying JupyterHub might be causing this!
Any pointers on how to fix this? I will start googling while awaiting your suggestion.

Cheers,
Salaken