Skip to content

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

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Closed
ferdinandbayard opened this issue Apr 11, 2025 · 5 comments
Labels
regression this used to work

Comments

@ferdinandbayard
Copy link

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 ?

@ndrezn ndrezn added the regression this used to work label Apr 11, 2025
@T4rk1n
Copy link
Contributor

T4rk1n commented Apr 14, 2025

What is the requests_pathname_prefix in this case ?

@ferdinandbayard
Copy link
Author

In this case, requests_pathname_prefix is :
/lab/user/{identifier}/proxy/{port}/

@T4rk1n
Copy link
Contributor

T4rk1n commented Apr 14, 2025

You need that for the proxy, but that is invalid for the alive check that is ran from the same process/machine. It got added in #3171 but should have been routes_pathname_prefix instead.

@ferdinandbayard
Copy link
Author

Thx for the investigation.

@T4rk1n
Copy link
Contributor

T4rk1n commented Apr 16, 2025

Fixed by #3281

@T4rk1n T4rk1n closed this as completed Apr 16, 2025
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
regression this used to work
Projects
None yet
Development

No branches or pull requests

3 participants