Repository: jupyterhub/jupyterhub

Issue:
Redirect loop after starting server
My JupyterHub installation uses GitHub authentication. It works well for a dozen users, however, for one user, myself, I enter a redirect loop when starting the server. It’s completely bizarre. The only difference between my user and the other users is that my username starts with a digit. Any ideas?



Discussion:
If it matters, I’m able to access the admin page without issue. I can also successfully start the server from the admin panel. However, when I try to access /hub/ I enter the redirect loop.

Do you see anything in the logs? What Spawner are you using?

@minrk I don't see anything unusual in the logs:

```
[I 2016-10-27 13:11:15.654 JupyterHub log:100] 302 GET / (@172.17.0.1) 4.66ms
[I 2016-10-27 13:11:15.894 JupyterHub log:100] 302 GET /hub (@172.17.0.1) 0.62ms
[D 2016-10-27 13:11:16.143 JupyterHub pages:35] User is not running: /hub/home
[I 2016-10-27 13:11:16.150 JupyterHub log:100] 302 GET /hub/ (0x00b1@172.17.0.1) 4.13ms
[I 2016-10-27 13:11:16.722 JupyterHub log:100] 200 GET /hub/home (0x00b1@172.17.0.1) 8.27ms
[I 2016-10-27 13:11:18.022 JupyterHub log:100] 302 GET /apple-touch-icon-precomposed.png (@172.17.0.1) 1.24ms
[W 2016-10-27 13:11:18.449 JupyterHub log:100] 404 GET /hub/apple-touch-icon-precomposed.png (0x00b1@172.17.0.1) 15.77ms
[I 2016-10-27 13:11:18.668 JupyterHub log:100] 302 GET /apple-touch-icon.png (@172.17.0.1) 1.21ms
[W 2016-10-27 13:11:18.838 JupyterHub log:100] 404 GET /hub/apple-touch-icon.png (0x00b1@172.17.0.1) 4.59ms
[I 2016-10-27 13:11:43.550 JupyterHub log:100] 302 GET /hub/spawn (0x00b1@172.17.0.1) 5.20ms
[I 2016-10-27 13:11:43.799 JupyterHub log:100] 302 GET /user/0x00b1 (@172.17.0.1) 1.25ms
[I 2016-10-27 13:11:44.169 JupyterHub spawner:467] Spawning jupyterhub-singleuser --user=0x00b1 --port=55108 --cookie-name=jupyter-hub-token-0x00b1 --base-url=/user/0x00b1 --hub-host= --hub-prefix=/hub/ --hub-api-url=http://127.0.0.1:8081/hub/api --ip=127.0.0.1
[D 2016-10-27 13:11:44.191 JupyterHub spawner:316] Polling subprocess every 30s
[I 2016-10-27 13:11:44.968 177 notebookapp:1128] Serving notebooks from local directory: /home/0x00b1
[I 2016-10-27 13:11:44.968 177 notebookapp:1128] 0 active kernels 
[I 2016-10-27 13:11:44.968 177 notebookapp:1128] The Jupyter Notebook is running at: http://127.0.0.1:55108/user/0x00b1/
[I 2016-10-27 13:11:44.968 177 notebookapp:1129] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[I 2016-10-27 13:11:45.027 177 log:47] 302 GET /user/0x00b1 (127.0.0.1) 0.62ms
[D 2016-10-27 13:11:45.028 JupyterHub utils:84] Server at http://127.0.0.1:55108/user/0x00b1 responded with 302
[I 2016-10-27 13:11:45.028 JupyterHub base:306] User 0x00b1 server took 0.914 seconds to start
[I 2016-10-27 13:11:45.028 JupyterHub orm:159] Adding user 0x00b1 to proxy /user/0x00b1 => http://127.0.0.1:55108
[D 2016-10-27 13:11:45.031 JupyterHub orm:146] Fetching POST http://127.0.0.1:8001/api/routes/user/0x00b1
[D 2016-10-27 13:11:45.035 JupyterHub base:231] Setting cookie for 0x00b1: jupyter-hub-token-0x00b1, {'secure': True}
[I 2016-10-27 13:11:45.047 JupyterHub log:100] 302 GET /hub/user/0x00b1 (0x00b1@172.17.0.1) 930.66ms
[I 2016-10-27 13:11:45.419 177 log:47] 302 GET /user/0x00b1 (172.17.0.1) 0.63ms
[I 2016-10-27 13:11:45.773 177 log:47] 302 GET /user/0x00b1/tree (172.17.0.1) 24.60ms
[I 2016-10-27 13:11:45.784 JupyterHub log:100] 200 GET /hub/api/authorizations/cookie/jupyter-hub-token-0x00b1/[secret] (0x00b1@127.0.0.1) 16.95ms
[D 2016-10-27 13:11:46.076 JupyterHub pages:31] User is running: /user/0x00b1
[D 2016-10-27 13:11:46.077 JupyterHub base:231] Setting cookie for 0x00b1: jupyter-hub-token-0x00b1, {'secure': True}
[I 2016-10-27 13:11:46.086 JupyterHub log:100] 302 GET /hub/?next=%2Fuser%2F0x00b1%2Ftree (0x00b1@172.17.0.1) 9.82ms
[I 2016-10-27 13:11:46.320 177 log:47] 302 GET /user/0x00b1 (172.17.0.1) 0.63ms
[I 2016-10-27 13:11:46.909 177 log:47] 302 GET /user/0x00b1/tree (172.17.0.1) 21.23ms
[I 2016-10-27 13:11:46.921 JupyterHub log:100] 200 GET /hub/api/authorizations/cookie/jupyter-hub-token-0x00b1/[secret] (0x00b1@127.0.0.1) 16.49ms
[D 2016-10-27 13:11:47.145 JupyterHub pages:31] User is running: /user/0x00b1
[D 2016-10-27 13:11:47.145 JupyterHub base:231] Setting cookie for 0x00b1: jupyter-hub-token-0x00b1, {'secure': True}
[I 2016-10-27 13:11:47.153 JupyterHub log:100] 302 GET /hub/?next=%2Fuser%2F0x00b1%2Ftree (0x00b1@172.17.0.1) 9.81ms
[I 2016-10-27 13:11:47.461 177 log:47] 302 GET /user/0x00b1 (172.17.0.1) 0.58ms
[I 2016-10-27 13:11:47.813 177 log:47] 302 GET /user/0x00b1/tree (172.17.0.1) 22.03ms
[I 2016-10-27 13:11:47.824 JupyterHub log:100] 200 GET /hub/api/authorizations/cookie/jupyter-hub-token-0x00b1/[secret] (0x00b1@127.0.0.1) 17.28ms
[D 2016-10-27 13:11:48.042 JupyterHub pages:31] User is running: /user/0x00b1
[D 2016-10-27 13:11:48.043 JupyterHub base:231] Setting cookie for 0x00b1: jupyter-hub-token-0x00b1, {'secure': True}
[I 2016-10-27 13:11:48.051 JupyterHub log:100] 302 GET /hub/?next=%2Fuser%2F0x00b1%2Ftree (0x00b1@172.17.0.1) 9.96ms
[I 2016-10-27 13:11:48.611 177 log:47] 302 GET /user/0x00b1 (172.17.0.1) 0.69ms
[I 2016-10-27 13:11:48.879 177 log:47] 302 GET /user/0x00b1/tree (172.17.0.1) 21.40ms
[I 2016-10-27 13:11:48.891 JupyterHub log:100] 200 GET /hub/api/authorizations/cookie/jupyter-hub-token-0x00b1/[secret] (0x00b1@127.0.0.1) 16.81ms
[D 2016-10-27 13:11:49.190 JupyterHub pages:31] User is running: /user/0x00b1
[D 2016-10-27 13:11:49.190 JupyterHub base:231] Setting cookie for 0x00b1: jupyter-hub-token-0x00b1, {'secure': True}
[I 2016-10-27 13:11:49.198 JupyterHub log:100] 302 GET /hub/?next=%2Fuser%2F0x00b1%2Ftree (0x00b1@172.17.0.1) 9.96ms
[I 2016-10-27 13:11:49.510 177 log:47] 302 GET /user/0x00b1 (172.17.0.1) 0.65ms
[I 2016-10-27 13:11:49.857 177 log:47] 302 GET /user/0x00b1/tree (172.17.0.1) 21.26ms
[I 2016-10-27 13:11:49.869 JupyterHub log:100] 200 GET /hub/api/authorizations/cookie/jupyter-hub-token-0x00b1/[secret] (0x00b1@127.0.0.1) 16.58ms
[D 2016-10-27 13:12:31.434 JupyterHub orm:146] Fetching GET http://127.0.0.1:8001/api/routes
[I 2016-10-27 13:13:27.334 JupyterHub log:100] 302 GET / (@172.17.0.1) 3.88ms
[I 2016-10-27 13:13:27.656 JupyterHub log:100] 302 GET /hub (@172.17.0.1) 0.65ms
[D 2016-10-27 13:13:28.077 JupyterHub pages:31] User is running: /user/0x00b1
[D 2016-10-27 13:13:28.077 JupyterHub base:231] Setting cookie for 0x00b1: jupyter-hub-token-0x00b1, {'secure': True}
[I 2016-10-27 13:13:28.086 JupyterHub log:100] 302 GET /hub/ (0x00b1@172.17.0.1) 7.40ms
[I 2016-10-27 13:13:28.156 JupyterHub log:100] 302 GET / (@172.17.0.1) 1.23ms
[I 2016-10-27 13:13:28.318 177 log:47] 302 GET /user/0x00b1 (172.17.0.1) 0.75ms
[I 2016-10-27 13:13:28.483 JupyterHub log:100] 302 GET /hub (@172.17.0.1) 0.59ms
[I 2016-10-27 13:13:28.666 177 log:47] 302 GET /user/0x00b1/tree (172.17.0.1) 23.06ms
[I 2016-10-27 13:13:28.677 JupyterHub log:100] 200 GET /hub/api/authorizations/cookie/jupyter-hub-token-0x00b1/[secret] (0x00b1@127.0.0.1) 18.08ms
[I 2016-10-27 13:13:28.819 JupyterHub log:100] 302 GET /hub/ (@172.17.0.1) 3.91ms
[D 2016-10-27 13:13:28.983 JupyterHub pages:31] User is running: /user/0x00b1
[D 2016-10-27 13:13:28.983 JupyterHub base:231] Setting cookie for 0x00b1: jupyter-hub-token-0x00b1, {'secure': True}
[I 2016-10-27 13:13:28.992 JupyterHub log:100] 302 GET /hub/?next=%2Fuser%2F0x00b1%2Ftree (0x00b1@172.17.0.1) 8.33ms
[I 2016-10-27 13:13:29.145 JupyterHub log:100] 200 GET /hub/login (@172.17.0.1) 2.48ms
[I 2016-10-27 13:13:29.308 177 log:47] 302 GET /user/0x00b1 (172.17.0.1) 0.66ms
[D 2016-10-27 13:13:29.472 JupyterHub log:100] 200 GET /hub/static/css/style.min.css?v=91c753d3c28f12b7480e5d0d9e7c55b2 (@172.17.0.1) 1.75ms
[I 2016-10-27 13:13:30.131 177 log:47] 302 GET /user/0x00b1/tree (172.17.0.1) 0.92ms
[D 2016-10-27 13:13:30.545 JupyterHub pages:31] User is running: /user/0x00b1
[D 2016-10-27 13:13:30.545 JupyterHub base:231] Setting cookie for 0x00b1: jupyter-hub-token-0x00b1, {'secure': True}
[I 2016-10-27 13:13:30.554 JupyterHub log:100] 302 GET /hub/?next=%2Fuser%2F0x00b1%2Ftree (0x00b1@172.17.0.1) 7.38ms
[D 2016-10-27 13:13:30.710 JupyterHub log:100] 200 GET /hub/static/components/requirejs/require.js?v=6da8be361b9ee26c5e721e76c6d4afce (@172.17.0.1) 1.53ms
[I 2016-10-27 13:13:30.873 177 log:47] 302 GET /user/0x00b1 (172.17.0.1) 0.65ms
[I 2016-10-27 13:13:31.226 177 log:47] 302 GET /user/0x00b1/tree (172.17.0.1) 23.10ms
[I 2016-10-27 13:13:31.237 JupyterHub log:100] 200 GET /hub/api/authorizations/cookie/jupyter-hub-token-0x00b1/[secret] (0x00b1@127.0.0.1) 16.75ms
[D 2016-10-27 13:13:31.278 JupyterHub log:100] 200 GET /hub/logo (@172.17.0.1) 1.06ms
[D 2016-10-27 13:13:31.699 JupyterHub pages:31] User is running: /user/0x00b1
[D 2016-10-27 13:13:31.700 JupyterHub base:231] Setting cookie for 0x00b1: jupyter-hub-token-0x00b1, {'secure': True}
[I 2016-10-27 13:13:31.708 JupyterHub log:100] 302 GET /hub/?next=%2Fuser%2F0x00b1%2Ftree (0x00b1@172.17.0.1) 10.00ms
[I 2016-10-27 13:13:31.938 177 log:47] 302 GET /user/0x00b1 (172.17.0.1) 0.60ms
[D 2016-10-27 13:13:31.939 JupyterHub log:100] 200 GET /favicon.ico (@172.17.0.1) 1.30ms
[I 2016-10-27 13:13:32.205 177 log:47] 302 GET /user/0x00b1/tree (172.17.0.1) 22.06ms
[I 2016-10-27 13:13:32.216 JupyterHub log:100] 200 GET /hub/api/authorizations/cookie/jupyter-hub-token-0x00b1/[secret] (0x00b1@127.0.0.1) 17.39ms
[D 2016-10-27 13:13:32.346 JupyterHub log:100] 200 GET /favicon.ico (@172.17.0.1) 1.21ms
[D 2016-10-27 13:13:32.521 JupyterHub pages:31] User is running: /user/0x00b1
[D 2016-10-27 13:13:32.521 JupyterHub base:231] Setting cookie for 0x00b1: jupyter-hub-token-0x00b1, {'secure': True}
[I 2016-10-27 13:13:32.530 JupyterHub log:100] 302 GET /hub/?next=%2Fuser%2F0x00b1%2Ftree (0x00b1@172.17.0.1) 10.02ms
[I 2016-10-27 13:13:32.767 177 log:47] 302 GET /user/0x00b1 (172.17.0.1) 0.66ms
[I 2016-10-27 13:13:33.029 177 log:47] 302 GET /user/0x00b1/tree (172.17.0.1) 23.13ms
[I 2016-10-27 13:13:33.041 JupyterHub log:100] 200 GET /hub/api/authorizations/cookie/jupyter-hub-token-0x00b1/[secret] (0x00b1@127.0.0.1) 18.45ms
[I 2016-10-27 13:13:34.979 JupyterHub oauth2:37] oauth redirect: 'https://graphics.imagi.ng:9100/hub/oauth_callback'
[I 2016-10-27 13:13:34.984 JupyterHub log:100] 302 GET /hub/oauth_login (@172.17.0.1) 4.25ms
[D 2016-10-27 13:13:35.723 JupyterHub base:231] Setting cookie for mcquin: jupyter-hub-token, {'secure': True}
[I 2016-10-27 13:13:35.727 JupyterHub log:100] 302 GET /hub/oauth_callback?code=d3c82ac7fb1850396137 (@172.17.0.1) 247.79ms
[I 2016-10-27 13:13:36.145 JupyterHub log:100] 200 GET /hub/home (mcquin@172.17.0.1) 8.24ms
[D 2016-10-27 13:13:36.463 JupyterHub log:100] 200 GET /hub/static/components/font-awesome/fonts/fontawesome-webfont.woff?v=4.1.0 (@172.17.0.1) 1.55ms
[D 2016-10-27 13:13:36.465 JupyterHub log:100] 200 GET /hub/static/js/home.js?v=(& (@172.17.0.1) 1.35ms
[D 2016-10-27 13:13:37.195 JupyterHub log:100] 200 GET /hub/static/components/jquery/jquery.min.js?v=(& (@172.17.0.1) 1.49ms
[D 2016-10-27 13:13:38.022 JupyterHub log:100] 200 GET /hub/static/js/jhapi.js?v=(& (@172.17.0.1) 1.30ms
[D 2016-10-27 13:13:38.350 JupyterHub log:100] 200 GET /hub/static/js/utils.js?v=(& (@172.17.0.1) 1.30ms
[D 2016-10-27 13:13:38.840 JupyterHub log:100] 304 GET /favicon.ico (@172.17.0.1) 1.05ms
[I 2016-10-27 13:13:39.834 JupyterHub log:100] 302 GET /hub/spawn (mcquin@172.17.0.1) 5.75ms
[I 2016-10-27 13:13:40.157 JupyterHub log:100] 302 GET /user/mcquin (@172.17.0.1) 1.26ms
[I 2016-10-27 13:13:40.700 JupyterHub spawner:467] Spawning jupyterhub-singleuser --user=mcquin --port=44400 --cookie-name=jupyter-hub-token-mcquin --base-url=/user/mcquin --hub-host= --hub-prefix=/hub/ --hub-api-url=http://127.0.0.1:8081/hub/api --ip=127.0.0.1
[D 2016-10-27 13:13:40.721 JupyterHub spawner:316] Polling subprocess every 30s
[I 2016-10-27 13:13:41.507 mcquin notebookapp:1128] Serving notebooks from local directory: /home/mcquin
[I 2016-10-27 13:13:41.507 mcquin notebookapp:1128] 0 active kernels 
[I 2016-10-27 13:13:41.507 mcquin notebookapp:1128] The Jupyter Notebook is running at: http://127.0.0.1:44400/user/mcquin/
[I 2016-10-27 13:13:41.507 mcquin notebookapp:1129] Use Control-C to stop this server and shut down all kernels (twice to skip confirmation).
[I 2016-10-27 13:13:41.558 mcquin log:47] 302 GET /user/mcquin (127.0.0.1) 1.02ms
[D 2016-10-27 13:13:41.559 JupyterHub utils:84] Server at http://127.0.0.1:44400/user/mcquin responded with 302
[I 2016-10-27 13:13:41.559 JupyterHub base:306] User mcquin server took 0.911 seconds to start
[I 2016-10-27 13:13:41.560 JupyterHub orm:159] Adding user mcquin to proxy /user/mcquin => http://127.0.0.1:44400
[D 2016-10-27 13:13:41.563 JupyterHub orm:146] Fetching POST http://127.0.0.1:8001/api/routes/user/mcquin
[D 2016-10-27 13:13:41.566 JupyterHub base:231] Setting cookie for mcquin: jupyter-hub-token-mcquin, {'secure': True}
[I 2016-10-27 13:13:41.578 JupyterHub log:100] 302 GET /hub/user/mcquin (mcquin@172.17.0.1) 927.69ms
[I 2016-10-27 13:13:41.792 mcquin log:47] 302 GET /user/mcquin (172.17.0.1) 0.73ms
[I 2016-10-27 13:13:42.237 JupyterHub log:100] 200 GET /hub/api/authorizations/cookie/jupyter-hub-token-mcquin/[secret] (mcquin@127.0.0.1) 16.96ms
[I 2016-10-27 13:13:52.554 mcquin handlers:170] Creating new notebook in 
[I 2016-10-27 13:13:52.562 mcquin sign:195] Writing notebook-signing key to /home/mcquin/.local/share/jupyter/notebook_secret
[I 2016-10-27 13:13:58.162 mcquin kernelmanager:89] Kernel started: b978fe3c-fcd8-4146-b6a7-eaee6f346ba1
[W 2016-10-27 13:13:58.187 mcquin log:47] 404 GET /user/mcquin/nbextensions/widgets/notebook/js/extension.js?v=20161027131341 (172.17.0.1) 15.09ms referer=https://graphics.imagi.ng:9100/user/mcquin/notebooks/Untitled.ipynb?kernel_name=python2
```

We are using the default spawner.

@minrk Mentioning this unicode issue here since it may relate: https://github.com/jupyterhub/jupyterhub/issues/444#issuecomment-256639192


Resolving patch:
diff --git a/jupyterhub/spawner.py b/jupyterhub/spawner.py
--- a/jupyterhub/spawner.py
+++ b/jupyterhub/spawner.py
@@ -298,15 +298,15 @@ def format_string(self, s):
     def get_args(self):
         """Return the arguments to be passed after self.cmd"""
         args = [
-            '--user=%s' % self.user.name,
-            '--cookie-name=%s' % self.user.server.cookie_name,
-            '--base-url=%s' % self.user.server.base_url,
-            '--hub-host=%s' % self.hub.host,
-            '--hub-prefix=%s' % self.hub.server.base_url,
-            '--hub-api-url=%s' % self.hub.api_url,
+            '--user="%s"' % self.user.name,
+            '--cookie-name="%s"' % self.user.server.cookie_name,
+            '--base-url="%s"' % self.user.server.base_url,
+            '--hub-host="%s"' % self.hub.host,
+            '--hub-prefix="%s"' % self.hub.server.base_url,
+            '--hub-api-url="%s"' % self.hub.api_url,
             ]
         if self.ip:
-            args.append('--ip=%s' % self.ip)
+            args.append('--ip="%s"' % self.ip)
 
         if self.port:
             args.append('--port=%i' % self.port)
@@ -316,10 +316,10 @@ def get_args(self):
 
         if self.notebook_dir:
             notebook_dir = self.format_string(self.notebook_dir)
-            args.append('--notebook-dir=%s' % notebook_dir)
+            args.append('--notebook-dir="%s"' % notebook_dir)
         if self.default_url:
             default_url = self.format_string(self.default_url)
-            args.append('--NotebookApp.default_url=%s' % default_url)
+            args.append('--NotebookApp.default_url="%s"' % default_url)
 
         if self.debug:
             args.append('--debug')


Test patch:
diff --git a/jupyterhub/tests/test_api.py b/jupyterhub/tests/test_api.py
--- a/jupyterhub/tests/test_api.py
+++ b/jupyterhub/tests/test_api.py
@@ -390,10 +390,10 @@ def test_spawn(app, io_loop):
     r = requests.get(ujoin(url, 'args'))
     assert r.status_code == 200
     argv = r.json()
-    for expected in ['--user=%s' % name, '--base-url=%s' % user.server.base_url]:
+    for expected in ['--user="%s"' % name, '--base-url="%s"' % user.server.base_url]:
         assert expected in argv
     if app.subdomain_host:
-        assert '--hub-host=%s' % app.subdomain_host in argv
+        assert '--hub-host="%s"' % app.subdomain_host in argv
 
     r = api_request(app, 'users', name, 'server', method='delete')
     assert r.status_code == 204