Jupyter "Error Starting Kernel" (500 Unhandled error): three causes, tested
Error Starting Kernel in Jupyter usually means the interpreter in the kernel's kernel.json is gone, lacks ipykernel, or the spec file is broken. The server answers 500 Unhandled error and only its log says why. All three cases are reproduced below, plus a script that checks every kernelspec.
1. Reproduce: four kernelspecs, three ways to fail
run.py writes four kernel specs (a working one, one pointing at a deleted virtualenv, one pointing at a Python without ipykernel, one with broken kernel.json), starts a Jupyter server, and asks it to start each kernel through the same REST call the notebook UI uses when you pick a kernel:
import json, os, subprocess, sys, time, urllib.request, urllib.error
# kernelspecs that fail in three different ways, plus one that works
D = '/tmp/ks412'
def spec(name, argv):
os.makedirs(f'{D}/data/kernels/{name}', exist_ok=True)
json.dump({'argv': argv, 'display_name': name, 'language': 'python'},
open(f'{D}/data/kernels/{name}/kernel.json', 'w'))
CF = ['-m', 'ipykernel_launcher', '-f', '{connection_file}']
spec('good', [sys.executable] + CF)
spec('deleted-venv', ['/tmp/ks412/oldenv/bin/python'] + CF) # path that no longer exists
spec('no-ipykernel', ['/usr/bin/python3'] + CF) # interpreter without ipykernel
spec('bad-json', [sys.executable] + CF)
open(f'{D}/data/kernels/bad-json/kernel.json', 'w').write('{"argv": [,]}')
os.makedirs(f'{D}/root', exist_ok=True)
env = dict(os.environ, JUPYTER_DATA_DIR=f'{D}/data', JUPYTER_RUNTIME_DIR=f'{D}/rt', JUPYTER_CONFIG_DIR=f'{D}/cfg',
JUPYTER_PATH=f'{D}/data')
srv = subprocess.Popen([sys.executable, '-m', 'jupyter_server', '--port=8963', '--no-browser',
'--IdentityProvider.token=abc123', f'--ServerApp.root_dir={D}/root'],
env=env, stdout=subprocess.DEVNULL, stderr=open(f'{D}/server.log', 'w'))
B = 'http://127.0.0.1:8963'
H = {'Authorization': 'token abc123', 'Content-Type': 'application/json'}
for _ in range(100):
try: urllib.request.urlopen(B + '/api', timeout=1); break
except Exception: time.sleep(0.2)
def call(method, path, body=None):
req = urllib.request.Request(B + path, method=method, headers=H,
data=json.dumps(body).encode() if body is not None else None)
try:
r = urllib.request.urlopen(req, timeout=30); return r.status, json.loads(r.read() or b'null')
except urllib.error.HTTPError as e:
return e.code, json.loads(e.read() or b'null')
st, specs = call('GET', '/api/kernelspecs')
print('kernelspecs listed:', sorted(specs['kernelspecs']))
for name in ['good', 'deleted-venv', 'no-ipykernel', 'bad-json']:
st, body = call('POST', '/api/kernels', {'name': name})
msg = body.get('message') if isinstance(body, dict) else body
print(f'POST /api/kernels {name!r}: {st}', (msg or '')[:110] if st != 201 else 'id=' + body['id'][:8] + ' state=' + body['execution_state'])
if st == 201:
last = None
for t in range(25):
st2, k = call('GET', '/api/kernels/' + body['id'])
cur = f'GET {st2} ' + (k['execution_state'] if st2 == 200 else k.get('message', ''))
if cur != last: print(f' after {t:>2}s: {cur}'); last = cur
if name == 'good' and t == 5: break
time.sleep(1)
srv.terminate()
try: srv.wait(timeout=5)
except subprocess.TimeoutExpired: srv.kill(); srv.wait()
print('--- server log:')
for line in open(f'{D}/server.log'):
if any(w in line for w in ('Error loading', 'Failed to run', 'Errno 2', 'No module', 'Restarter', 'restart', 'dead', '500 POST', 'Expecting value')):
print(line.rstrip()[:150].replace(line[3:27], 'TIME ') if line.startswith('[') else line.rstrip()[:150])
Output (jupyter_server 2.21.1, jupyter_client 8.10.0, ipykernel 7.4.0, Python 3.13 on Linux):
kernelspecs listed: ['deleted-venv', 'good', 'no-ipykernel', 'python3']
POST /api/kernels 'good': 201 id=fd3141f0 state=starting
after 0s: GET 200 starting
POST /api/kernels 'deleted-venv': 500 Unhandled error
POST /api/kernels 'no-ipykernel': 201 id=cc8d1bbd state=starting
after 0s: GET 200 starting
after 18s: GET 404 Kernel does not exist: cc8d1bbd-bdcc-489c-926e-875be586e604
POST /api/kernels 'bad-json': 500 Unhandled error
--- server log:
[W TIME ServerApp] Error loading kernelspec 'bad-json'
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 11 (char 10)
[E TIME ServerApp] Failed to run command:
[E TIME ServerApp] [Errno 2] No such file or directory: '/tmp/ks412/oldenv/bin/python'
FileNotFoundError: [Errno 2] No such file or directory: '/tmp/ks412/oldenv/bin/python'
[E TIME ServerApp] [Errno 2] No such file or directory: '/tmp/ks412/oldenv/bin/python'
FileNotFoundError: [Errno 2] No such file or directory: '/tmp/ks412/oldenv/bin/python'
FileNotFoundError: [Errno 2] No such file or directory: '/tmp/ks412/oldenv/bin/python'
[E TIME ServerApp] 500 POST /api/kernels (929623700111435bb2c724150e86199d@127.0.0.1) 14.51ms referer=None
/usr/bin/python3: No module named ipykernel_launcher
[I TIME ServerApp] AsyncIOLoopKernelRestarter: restarting kernel (1/5), new random ports
/usr/bin/python3: No module named ipykernel_launcher
[I TIME ServerApp] AsyncIOLoopKernelRestarter: restarting kernel (2/5), new random ports
/usr/bin/python3: No module named ipykernel_launcher
[I TIME ServerApp] AsyncIOLoopKernelRestarter: restarting kernel (3/5), new random ports
/usr/bin/python3: No module named ipykernel_launcher
[I TIME ServerApp] AsyncIOLoopKernelRestarter: restarting kernel (4/5), new random ports
/usr/bin/python3: No module named ipykernel_launcher
[I TIME ServerApp] AsyncIOLoopKernelRestarter: restarting kernel (5/5), new random ports
/usr/bin/python3: No module named ipykernel_launcher
[W TIME ServerApp] AsyncIOLoopKernelRestarter: restart failed
[E TIME ServerApp] Expecting value: line 1 column 11 (char 10)
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 11 (char 10)
[E TIME ServerApp] Expecting value: line 1 column 11 (char 10)
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 11 (char 10)
raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 11 (char 10)
[E TIME ServerApp] 500 POST /api/kernels (b1c6d42326eb4947b23dcd2feac0cc1d@127.0.0.1) 10.23ms referer=None
2. Which failure you have
- Interpreter path no longer exists (
deleted-venv): the start request fails at once with 500 and the body says onlyUnhandled error; this is the failure a notebook UI reports as "Error Starting Kernel". The real cause is in the server log:Failed to run commandplusFileNotFoundError: [Errno 2] No such file or directorywith thepythonpath fromkernel.json. Typical after deleting or moving a venv/conda env that was registered withipykernel install. - Interpreter has no ipykernel (
no-ipykernel): the start request succeeds (201, statestarting), the process exits withNo module named ipykernel_launcher, the restarter tries 5 times, logsrestart failed, and about 18 seconds later the kernel no longer exists (404). So in a notebook the kernel never becomes ready and then disappears. - Broken kernel.json (
bad-json): the spec is silently missing fromGET /api/kernelspecs(only a warningError loading kernelspec 'bad-json'in the log), and starting it by name gives the same 500Unhandled errorwith aJSONDecodeErrorin the log.
So for "Error Starting Kernel" with no details, the browser can't tell you more: read the terminal where jupyter lab / jupyter notebook runs, or its log.
3. Check every kernelspec at once
check.py loads each spec the way Jupyter does, then checks the interpreter exists and can import ipykernel. A bare python in argv is fine: jupyter_client replaces it with the server's own interpreter (sys.executable):
# check every kernelspec Jupyter can see: does argv[0] exist, and can it import ipykernel?
import os, subprocess, sys
from jupyter_client.kernelspec import KernelSpecManager
ksm = KernelSpecManager()
for name, path in sorted(ksm.find_kernel_specs().items()):
try:
argv = ksm.get_kernel_spec(name).argv
except Exception as e:
print(f'{name:13} BROKEN kernel.json: {type(e).__name__}: {e}'); continue
exe = argv[0]
if exe in ('python', 'python3', f'python{sys.version_info[0]}.{sys.version_info[1]}'):
exe = sys.executable # jupyter_client swaps a bare 'python' for the server's own interpreter
if not os.path.exists(exe):
print(f'{name:13} MISSING interpreter {exe}'); continue
r = subprocess.run([exe, '-c', 'import ipykernel; print(ipykernel.__version__)'], capture_output=True, text=True)
print(f'{name:13}', f'OK ipykernel {r.stdout.strip()}' if r.returncode == 0 else f'NO ipykernel in {exe}')
bad-json BROKEN kernel.json: JSONDecodeError: Expecting value: line 1 column 11 (char 10)
deleted-venv MISSING interpreter /tmp/ks412/oldenv/bin/python
good OK ipykernel 7.4.0
no-ipykernel NO ipykernel in /usr/bin/python3
python3 OK ipykernel 7.4.0
4. Fix
jupyter kernelspec remove -y deleted-venv # the env is gone: drop its spec
/usr/bin/python3 -m ipykernel --version # this interpreter has no ipykernel
jupyter kernelspec list 2>/dev/null # (stderr still warns about bad-json)
Removed /tmp/ks412/data/kernels/deleted-venv
/usr/bin/python3: No module named ipykernel
Available kernels:
bad-json /tmp/ks412/data/kernels/bad-json
good /tmp/ks412/data/kernels/good
no-ipykernel /tmp/ks412/data/kernels/no-ipykernel
python3 /tmp/jn408/ve/share/jupyter/kernels/python3
- Missing interpreter:
jupyter kernelspec remove -y NAME, then recreate the env and register it again from inside it:python -m pip install ipykernelandpython -m ipykernel install --user --name NAME. - No ipykernel: install it into the interpreter the spec points at (
/path/to/python -m pip install ipykernel), not into whateverpipis first on PATH. - Broken kernel.json: note that
jupyter kernelspec liststill lists it (with a traceback on stderr) even though the server hides it; fix the JSON or remove the directory.
Related: Jupyter kernel not found (NoSuchKernel), Jupyter kernel keeps dying, Jupyter 403 Forbidden.
Frequently asked questions
Why does Jupyter say "Error Starting Kernel"?
The server could not launch the command in the kernel's kernel.json, most often because that Python interpreter (a deleted venv or conda env) no longer exists. The 500 response only says "Unhandled error"; the server log shows FileNotFoundError with the path.
Why is my Jupyter kernel stuck on starting and then dies?
The interpreter exists but has no ipykernel: it exits with "No module named ipykernel_launcher", Jupyter restarts it 5 times, then gives up (about 18 seconds in our test). Install ipykernel into that interpreter.
How do I find which kernel is broken?
Run jupyter kernelspec list and check each argv path in kernel.json exists and can import ipykernel, or use the check script in this guide.
Written with AI assistance. Measured on 2026-10-23 with jupyter_server 2.21.1, jupyter_client 8.10.0, ipykernel 7.4.0 and Python 3.13 on Linux. The scripts ran verbatim and the output was pasted unedited. PlainTables is our extension; Jupyter is free.