Skip to content

[BUG] Dash.run() raises OSError "Address already in use" when ugrading to dash 3 in Jupyter #3270

Closed
@ferdinandbayard

Description

@ferdinandbayard

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 ?

Metadata

Metadata

Assignees

No one assigned

    Labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions