Closed
Description
I got an error on Dash.run() when using it in Jupyter environment, behind proxy.
This error happens when upgrading dash from 2.18 to 3.0.2.
Environment
I use conda-forge to install packages.
name: visuconti-env
dependencies:
- python=3.13
- jupyterlab
- jupyterhub
- dash=3.0.2 #2.18
- jupyter-server-proxy
Context
The problem can be reproduced on "Hello world" dash.
from dash import Dash, dcc, html, Input, Output, jupyter_dash
import plotly.graph_objects as go
import dash
app = Dash(__name__)
app.layout = html.Div([
html.H4('Interactive color selection with simple Dash example'),
html.P("Select color:"),
dcc.Dropdown(
id="dropdown",
options=['Gold', 'MediumTurquoise', 'LightGreen'],
value='Gold',
clearable=False,
),
dcc.Graph(id="graph"),
])
@app.callback(
Output("graph", "figure"),
Input("dropdown", "value"))
def display_color(color):
fig = go.Figure(
data=go.Bar(y=[2, 3, 1], # replace with your own data source
marker_color=color))
return fig
jupyter_dash.infer_jupyter_proxy_config()
app.run(debug=True, port=8056)
Error
The error is raised by the app.run(), whatever the port argument.
---------------------------------------------------------------------------
OSError Traceback (most recent call last)
Cell In[1], line 25
23 return fig
24 jupyter_dash.infer_jupyter_proxy_config()
---> 25 app.run(debug=True, port=8056)
File /opt/conda/envs/visuconti-env/lib/python3.13/site-packages/dash/dash.py:2257, in Dash.run(self, host, port, proxy, debug, jupyter_mode, jupyter_width, jupyter_height, jupyter_server_url, dev_tools_ui, dev_tools_props_check, dev_tools_serve_dev_bundles, dev_tools_hot_reload, dev_tools_hot_reload_interval, dev_tools_hot_reload_watch_interval, dev_tools_hot_reload_max_retry, dev_tools_silence_routes_logging, dev_tools_disable_version_check, dev_tools_prune_errors, **flask_run_options)
2254 extra_files.append(path)
2256 if jupyter_dash.active:
-> 2257 jupyter_dash.run_app(
2258 self,
2259 mode=jupyter_mode,
2260 width=jupyter_width,
2261 height=jupyter_height,
2262 host=host,
2263 port=port,
2264 server_url=jupyter_server_url,
2265 )
2266 else:
2267 self.server.run(host=host, port=port, debug=debug, **flask_run_options)
File /opt/conda/envs/visuconti-env/lib/python3.13/site-packages/dash/_jupyter.py:405, in JupyterDash.run_app(self, app, mode, width, height, host, port, server_url)
403 display(HTML(msg))
404 else:
--> 405 raise final_error
File /opt/conda/envs/visuconti-env/lib/python3.13/site-packages/dash/_jupyter.py:392, in JupyterDash.run_app(self, app, mode, width, height, host, port, server_url)
389 raise err
391 try:
--> 392 wait_for_app()
394 if self.in_colab:
395 JupyterDash._display_in_colab(dashboard_url, port, mode, width, height)
File /opt/conda/envs/visuconti-env/lib/python3.13/site-packages/retrying.py:56, in retry.<locals>.wrap.<locals>.wrapped_f(*args, **kw)
54 @six.wraps(f)
55 def wrapped_f(*args, **kw):
---> 56 return Retrying(*dargs, **dkw).call(f, *args, **kw)
File /opt/conda/envs/visuconti-env/lib/python3.13/site-packages/retrying.py:266, in Retrying.call(self, fn, *args, **kwargs)
263 if self.stop(attempt_number, delay_since_first_attempt_ms):
264 if not self._wrap_exception and attempt.has_exception:
265 # get() on an attempt with an exception should cause it to be raised, but raise just in case
--> 266 raise attempt.get()
267 else:
268 raise RetryError(attempt)
File /opt/conda/envs/visuconti-env/lib/python3.13/site-packages/retrying.py:301, in Attempt.get(self, wrap_exception)
299 raise RetryError(self)
300 else:
--> 301 six.reraise(self.value[0], self.value[1], self.value[2])
302 else:
303 return self.value
File /opt/conda/envs/visuconti-env/lib/python3.13/site-packages/six.py:724, in reraise(tp, value, tb)
722 if value.__traceback__ is not tb:
723 raise value.with_traceback(tb)
--> 724 raise value
725 finally:
726 value = None
File /opt/conda/envs/visuconti-env/lib/python3.13/site-packages/retrying.py:251, in Retrying.call(self, fn, *args, **kwargs)
248 self._before_attempts(attempt_number)
250 try:
--> 251 attempt = Attempt(fn(*args, **kwargs), attempt_number, False)
252 except:
253 tb = sys.exc_info()
File /opt/conda/envs/visuconti-env/lib/python3.13/site-packages/dash/_jupyter.py:383, in JupyterDash.run_app.<locals>.wait_for_app()
381 if res != "Alive":
382 url = f"[http://{host}:{](http://{host}:{port/)[port](http://{host}:{port/)}"
--> 383 raise OSError(
384 f"Address '{url}' already in use[.\n](https://feevisu-dev.cmp.retd.edf.fr/lab/user/fb45dddl/lab/workspaces/auto-c/tree/n)"
385 " Try passing a different port to run."
386 )
387 except requests.ConnectionError as err:
388 _get_error()
OSError: Address 'http://127.0.0.1:8056/' already in use.
Try passing a different port to run.
Investigation
The breaking change seems to be located at line 357 dash/_jupyter.py file. When manually replacing the line by the 2.18 version, it works again.
Dash3.0.2
alive_url = f"http://{host}:{port}{requests_pathname_prefix}alive{JupyterDash.alive_token}"
Dash2.18
alive_url = f"http://{host}:{port}/alive{JupyterDash.alive_token}"
** Question **
Should I find a less hacky way to debug this ?