Skip to content

Fix: Jupyter Notebook Not Working — Kernel Dead, Module Not Found, and Widget Errors

FixDevs · (Updated: )

Part of:  Python Errors

Quick Answer

How to fix Jupyter errors — kernel fails to start or dies, ModuleNotFoundError despite pip install, matplotlib plots not showing, ipywidgets not rendering in JupyterLab, port already in use, and jupyter command not found.

The Error

You open a notebook and the kernel immediately dies:

The kernel has died, and the automatic restart has failed.

Or you install a package and it still can’t be found:

import pandas
ModuleNotFoundError: No module named 'pandas'

— even though pip install pandas ran successfully a moment ago.

Or your plot code runs without error but nothing appears in the output cell:

import matplotlib.pyplot as plt
plt.plot([1, 2, 3])
# Cell completes. No output.

Or widgets render as raw text instead of interactive controls:

HBox(children=(IntProgress(value=0, max=100), HTML(value='')))

Jupyter sits at the intersection of Python environments, browser rendering, and kernel processes — so errors can originate from any of these three layers. This guide separates them clearly.

Why This Happens

Jupyter runs a kernel (a Python process) separate from the browser frontend. The kernel uses whichever Python binary it was registered with — not necessarily the one you ran pip install with. Packages installed by pip go into the Python environment that pip belongs to; if the kernel uses a different Python, those packages aren’t visible.

Browser-rendered outputs (plots, widgets) require specific notebook extensions to be enabled. Installing ipywidgets with pip isn’t enough — the frontend extension must also be present and compatible with your Jupyter version.

Fix 1: Kernel Fails to Start or Dies

Failed to start the kernel process.
The kernel has died, and the automatic restart has failed.

Step 1: Confirm ipykernel is installed in the right environment:

# Activate your virtual environment first, then:
pip install ipykernel

# Register it as a kernel Jupyter can find
python -m ipykernel install --user --name=myenv --display-name "Python (myenv)"

After registering, the kernel shows up in the Kernel menu as “Python (myenv)”. Select it and restart.

Step 2: Run with debug logging to see the actual crash reason:

jupyter notebook --debug 2>&1 | head -100
# or for JupyterLab
jupyter lab --debug

Look for the real error above the generic “kernel died” message — it’s usually a missing package, a corrupted ipykernel install, or a Python binary that no longer exists.

Step 3: Check for kernel conflicts. List registered kernels:

jupyter kernelspec list

Sample output:

Available kernels:
  python3    /usr/local/share/jupyter/kernels/python3
  myenv      /home/user/.local/share/jupyter/kernels/myenv

If a kernel points to a Python binary that was deleted (e.g., after removing a venv), remove it:

jupyter kernelspec remove myenv

Then re-register if needed.

Step 4: Kernel dies immediately on import — usually a native extension crash. Run the import in terminal first to see the real error:

python -c "import tensorflow"
# Or whatever import crashes the kernel

If it errors in terminal, fix the package error there. If it works in terminal but crashes the kernel, the kernel is using a different Python (see Fix 2).

Step 5: Memory OOM kills the kernel — your code allocates more RAM than available. This is silent: the kernel process is killed by the OS, which Jupyter reports as “kernel died.” Track memory in a cell:

import psutil
process = psutil.Process()
print(f"Memory: {process.memory_info().rss / 1024**3:.2f} GB")

Reduce batch sizes, load data in chunks, or add more swap space.

Fix 2: ModuleNotFoundError Despite pip install

This is the most common Jupyter confusion. You run pip install numpy in the terminal, then import numpy fails in the notebook. The cause: your terminal’s pip and the notebook’s kernel belong to different Python environments.

The definitive fix — run pip from inside the notebook, using the same Python as the kernel:

# In a notebook cell — installs into the same Python the kernel uses
import sys
!{sys.executable} -m pip install numpy

{sys.executable} expands to the full path of the Python binary running the kernel. This guarantees the package lands where the kernel can find it.

Understand why !pip install can fail:

# In a notebook cell:
!which pip       # May point to /usr/bin/pip (system)
!which python    # May point to /usr/bin/python (system)

import sys
print(sys.executable)   # /home/user/.venv/bin/python  ← the actual kernel Python

If which pip and sys.executable point to different Python installations, any !pip install installs into the wrong place.

Register your virtual environment as a Jupyter kernel — the permanent solution:

# 1. Activate your environment
source .venv/bin/activate         # Linux/macOS
.venv\Scripts\activate            # Windows

# 2. Install ipykernel inside the environment
pip install ipykernel

# 3. Register it
python -m ipykernel install --user --name=myproject --display-name "My Project"

# 4. Start Jupyter from outside the venv (or inside — doesn't matter)
jupyter lab

Now select “My Project” from the kernel list. All packages in .venv are available.

Pro Tip: Never rely on the default “Python 3” kernel if you’re using virtual environments. Register every project environment as its own named kernel. It takes 30 seconds and eliminates this confusion permanently. List your registered kernels any time with jupyter kernelspec list.

For the broader Python virtual environment confusion, see Python ModuleNotFoundError in venv.

Fix 3: Matplotlib Plots Not Displaying

import matplotlib.pyplot as plt
plt.plot([1, 2, 3])
# Nothing appears

Matplotlib needs a display backend directive that tells Jupyter how to render figures.

For classic Jupyter Notebook:

%matplotlib inline   # Static PNG in output cell — most common

Put this in the first cell of the notebook, before any plotting code. Inline backend renders the figure when the cell finishes executing.

For interactive plots in Jupyter Notebook:

%matplotlib notebook   # Interactive (zoom, pan) in classic notebook

For JupyterLab:

# Static inline (works out of the box)
%matplotlib inline

# Interactive (requires ipympl)
%pip install ipympl
%matplotlib widget

plt.show() is usually wrong in notebooks — it flushes the figure buffer, which in some backends closes the figure before it can be rendered in the cell output:

import matplotlib.pyplot as plt

# WRONG — plt.show() can produce blank output in some Jupyter backends
plt.plot([1, 2, 3])
plt.show()

# CORRECT — let the cell end naturally; Jupyter captures the figure automatically
plt.plot([1, 2, 3])
# Cell ends here — figure renders in output

If you need explicit display control (e.g., multiple figures per cell), use plt.figure() to separate them:

import matplotlib.pyplot as plt

fig1, ax1 = plt.subplots()
ax1.plot([1, 2, 3], label='line 1')
ax1.legend()
plt.show()   # Explicitly show fig1

fig2, ax2 = plt.subplots()
ax2.scatter([1, 2, 3], [3, 1, 2], color='red')
plt.show()   # Show fig2

Figures vanish after plt.close() — make sure you’re not clearing figures in a loop before the cell renders:

# WRONG — closes figures before output renders
for i in range(3):
    plt.plot([i, i+1, i+2])
    plt.savefig(f'fig_{i}.png')
    plt.close()   # Destroys the figure

# CORRECT — collect axes instead of closing in a loop
fig, axes = plt.subplots(1, 3, figsize=(12, 4))
for i, ax in enumerate(axes):
    ax.plot([i, i+1, i+2])
    ax.set_title(f'Plot {i}')
plt.tight_layout()

Fix 4: ipywidgets Not Rendering in JupyterLab

You install ipywidgets and tqdm or other widget-based libraries, but they display as raw text:

HBox(children=(IntProgress(value=0, max=100), HTML(value='')))

The Python package and the frontend extension are two separate pieces that must both be installed.

For classic Jupyter Notebook (7.x and earlier):

pip install ipywidgets
jupyter nbextension enable --py widgetsnbextension --sys-prefix
# Verify it's enabled
jupyter nbextension list

For JupyterLab 3.x and 4.x:

pip install ipywidgets
# JupyterLab 3+ auto-discovers pip-installed extensions — no extra step needed
# Verify:
jupyter labextension list

If the extension still doesn’t appear, try a full reinstall:

pip uninstall ipywidgets widgetsnbextension jupyterlab-widgets
pip install ipywidgets   # Re-installs all three

Restart the JupyterLab server (not just the kernel) after installing extensions. The frontend must reload to pick up new extensions.

tqdm progress bars specifically:

# WRONG — uses wrong tqdm variant
from tqdm import tqdm
for i in tqdm(range(100)):
    pass

# CORRECT for Jupyter — uses the auto-detecting variant
from tqdm.auto import tqdm
for i in tqdm(range(100)):
    pass

tqdm.auto automatically selects tqdm.notebook when running inside Jupyter, which renders as a proper HTML progress bar instead of plain text.

Fix 5: Stale Variables and Wrong Output

Notebooks don’t execute sequentially by definition — you can run cells in any order, and variables accumulate in the kernel’s memory across runs. This causes bugs that are hard to reproduce and easy to miss.

Scenario: You define df in cell 2, delete that cell, then run a later cell that uses df. It works — because df is still in kernel memory from the previous run. You share the notebook with a colleague, they run it top-to-bottom, and it fails because cell 2 is gone.

Common Mistake: Sharing a notebook that “works on your machine” but fails for others. The cause is almost always stale kernel state — a variable defined in a deleted cell, or a module imported in a previous session. A notebook is only reproducible if it runs cleanly from top to bottom on a fresh kernel.

Always test with Restart and Run All:

  • Jupyter Notebook: Kernel → Restart & Run All
  • JupyterLab: Run → Restart Kernel and Run All Cells

This clears all variables and re-executes every cell in order from the top. If the notebook fails during this, it’s broken.

Manually clear specific variables:

del df        # Delete one variable
del x, y, z   # Delete multiple

Clear everything:

%reset         # Prompts for confirmation
%reset -f      # No confirmation

Check what’s currently in memory:

%whos          # All variables with type and size
%who           # Variable names only

%autoreload for development — automatically reloads imported modules when their source files change:

%load_ext autoreload
%autoreload 2   # Reload all modules before each cell execution

import my_module   # Changes to my_module.py are picked up automatically

Without %autoreload 2, changes to a .py file require restarting the kernel for the import to reflect the update.

Fix 6: jupyter: command not found After Installation

$ jupyter notebook
bash: jupyter: command not found

Pip installs Jupyter’s script into a directory that isn’t on your PATH. The fix is always to run via python -m:

python -m jupyter notebook
python -m jupyter lab

This bypasses the PATH issue entirely by invoking Jupyter as a Python module.

To find where Jupyter was installed:

pip show jupyter   # Shows 'Location:' — the scripts are in ../bin/ from there

# On Linux/macOS:
python -m site --user-scripts   # ~/.local/bin on most systems

# Add to PATH permanently (bash):
echo 'export PATH="$HOME/.local/bin:$PATH"' >> ~/.bashrc
source ~/.bashrc

On Windows, pip installs scripts to %APPDATA%\Python\Python3x\Scripts. Add this to your PATH via System Properties → Environment Variables. Or just use python -m jupyter notebook.

With uv or pipx (recommended for tools you want globally accessible):

# Install Jupyter globally with uv
uv tool install jupyterlab

# Or with pipx
pipx install jupyterlab

Both manage isolated environments and automatically add the jupyter command to your PATH.

Fix 7: Address Already in Use

OSError: [Errno 98] Address already in use

A previous Jupyter server is still running (or a process took port 8888). Three options:

Option 1: Use a different port:

jupyter notebook --port 8890
jupyter lab --port 8890

Option 2: Find and kill the existing Jupyter server:

# List all running Jupyter servers
jupyter notebook list
# or
jupyter server list

# Output:
# Currently running servers:
# http://localhost:8888/?token=abc123 :: /home/user/projects

# Kill it
jupyter notebook stop 8888

Option 3: Find the process using the port directly:

# Linux/macOS
lsof -i :8888   # Find PID
kill -9 <PID>

# Windows (PowerShell)
netstat -ano | findstr :8888   # Find PID in last column
taskkill /PID <PID> /F

Fix 8: JupyterLab Extension Not Working

Extension error: JupyterLab X.Y requires @org/extension@>=A.B.C
Build is required: Please rebuild JupyterLab.

JupyterLab 3.x and later ship extensions as pip packages (prebuilt). No Node.js or build step required. If you’re seeing build errors, you may be on a very old JupyterLab, or the extension only supports the old system.

Check what’s installed and enabled:

jupyter labextension list   # Lists all extensions and their enabled status
pip list | grep jupyter      # See all jupyter-related pip packages

For modern JupyterLab (3.x+) — install extension via pip:

# Example: install the Variable Inspector
pip install lckr-jupyterlab-variableinspector

# Restart JupyterLab — extensions are discovered automatically

For legacy extensions that still require a build:

# Requires Node.js
npm install -g nodeenv
jupyter lab build

If extensions are broken after a JupyterLab upgrade, rebuild from scratch:

pip install --upgrade jupyterlab
jupyter lab clean       # Removes cached build artifacts
jupyter lab build       # Only needed for legacy extension system

Disable a broken extension without uninstalling it:

jupyter labextension disable @org/broken-extension

Still Not Working?

Trust the Notebook

Jupyter’s security model marks notebooks from untrusted sources as untrusted — JavaScript outputs are suppressed to prevent malicious code from running in your browser. If all outputs are blank but the kernel runs fine:

# Trust a specific notebook
jupyter trust notebook.ipynb

# Or from inside the notebook
from IPython.display import display
# Re-run cells — trusted notebooks render JS output

Export to PDF via nbconvert

jupyter nbconvert --to pdf notebook.ipynb

PDF export requires Pandoc and LaTeX:

# macOS
brew install pandoc
brew install --cask mactex-no-gui

# Ubuntu/Debian
sudo apt install pandoc texlive-xetex texlive-fonts-recommended

# Windows: install Pandoc from pandoc.org, then MiKTeX from miktex.org

For HTML export (no LaTeX required): jupyter nbconvert --to html notebook.ipynb.

Remote Jupyter Over SSH

If you run Jupyter on a remote server and access it in a local browser:

# On the remote server — start Jupyter without opening a browser
jupyter lab --no-browser --port=8888

# On your local machine — forward the remote port
ssh -L 8888:localhost:8888 user@remote-server

# Open http://localhost:8888 in your local browser
# Use the token shown in the remote server's terminal output

Large Output Cells Slowing Down the Notebook

When a cell produces megabytes of output (e.g., logging from a long training run), the notebook file and browser rendering suffer. Clear outputs before saving:

# Strip output from notebook file
jupyter nbconvert --to notebook --ClearOutputPreprocessor.enabled=True notebook.ipynb

Or use %%capture to suppress noisy cell output:

%%capture
# All stdout/stderr from this cell is suppressed
model.fit(X_train, y_train, verbose=1)

scikit-learn and NumPy in Notebooks

If sklearn estimators raise shape or dtype errors inside a notebook, confirm the data came through transformations correctly with display(X.shape) and display(df.dtypes). For sklearn Pipeline and NaN errors, see scikit-learn not working. For NumPy broadcasting and dtype issues that surface during data preparation in notebooks, see NumPy not working.

Notebook File Gets Corrupted on Save

When .ipynb JSON becomes unparseable (you see “Notebook does not appear to be JSON”), the usual cause is two clients writing the same file simultaneously — VS Code and a browser tab both autosaving, or a Git merge resolving the binary as text. Open the file in a text editor and run python -m json.tool notebook.ipynb to find the line where parsing breaks. Most “corruption” is a trailing comma or duplicate "outputs" key that you can hand-fix. Going forward, enable nbstripout as a Git filter so the diff is small and merges no longer poison the JSON.

Kernel Idle But Cells Never Finish

A cell stuck on the “executing” indicator while the kernel status reads “idle” means the websocket between browser and server dropped. Refresh the page — the kernel keeps running and re-attaches. If the cell genuinely never returns, run jupyter server list and curl http://localhost:8888/api/sessions to see active sessions; a session with last_activity from hours ago is wedged and should be killed with jupyter notebook stop 8888. Behind a reverse proxy (nginx, Cloudflare), set websocket timeouts to at least 60 seconds — most “stuck cell” reports turn out to be a 30-second proxy timeout.

import Resolves Different Versions in Notebook vs Terminal

sys.path in a Jupyter kernel includes the current working directory of the notebook first, before site-packages. A local pandas.py file or a utils/ folder next to the notebook will shadow the installed package. Print pandas.__file__ from a notebook cell and compare with the same from the terminal — if they differ, rename the local file or remove its directory from sys.path. This trap is most common when downloading example code that includes a numpy.py or scipy.py in the repo root.

Platform-Specific Differences

Jupyter is not one product — it is a family of related applications (JupyterLab, classic Notebook 6, Notebook 7, JupyterHub, VS Code’s notebook editor, Colab, Binder) that share a kernel protocol but diverge on extensions, kernel discovery, and frontend behavior. The same .ipynb can render perfectly in one and break in another.

JupyterLab vs Notebook 7 vs Notebook 6 Legacy

JupyterLab (current default, 4.x) is the long-term direction. Extensions are pip-installable, the UI is multi-panel, and ipywidgets works without enabling nbextensions.

Notebook 7 is a complete rewrite of the classic UI on top of JupyterLab components — jupyter notebook now launches Notebook 7 unless you install nbclassic separately. The single-file UI is preserved, but the extension system is identical to JupyterLab. Many tutorials that say “enable widgetsnbextension” no longer apply.

Notebook 6 legacy still exists as the nbclassic package. Use it only when you need an old extension (e.g., RISE slideshows) that was never ported. pip install nbclassic; jupyter nbclassic launches the classic UI alongside JupyterLab. Mixing the two on the same port causes the kernel-already-running errors covered in Fix 7.

JupyterHub vs Binder vs Colab vs VS Code Notebook

  • JupyterHub runs one JupyterLab per user behind a shared proxy. Authentication is delegated (OAuth, LDAP, PAM), and storage is per-user via spawner classes (DockerSpawner, KubeSpawner). Most “my notebook works locally but not on the Hub” issues are spawner config — the user pod has a different image than the admin tested.
  • Binder spins up an ephemeral JupyterLab from a Git repo by building a Docker image from your environment.yml / requirements.txt / apt.txt. Sessions die after 10 minutes of inactivity and lose all changes. Use it for demos, never for active work.
  • Google Colab is a Jupyter fork with a custom frontend and a pinned (older) Python — packages installed via !pip install survive only the current session, and %pip install is preferred to avoid the shell-vs-kernel environment mismatch. Colab does not implement the standard widgetsnbextension either; the GPU runtime resets after 12 hours.
  • VS Code Notebook editor runs the kernel locally (or over SSH/WSL) but renders in Electron rather than a browser. %matplotlib widget does not work — use %matplotlib inline. The kernel picker reads from jupyter kernelspec list plus VS Code’s own Python interpreter discovery, which is why a kernel registered with --user can appear twice. For matplotlib-specific rendering issues that change depending on the frontend, see matplotlib not working.

Kernel Discovery Per Platform

Jupyter finds kernels by scanning kernel.json files in platform-specific directories:

  • Linux: ~/.local/share/jupyter/kernels/ (user) and /usr/local/share/jupyter/kernels/ (system)
  • macOS: ~/Library/Jupyter/kernels/ (user) and /Library/Jupyter/kernels/ (system)
  • Windows: %APPDATA%\jupyter\kernels\ (user) and %PROGRAMDATA%\jupyter\kernels\ (system)

Run jupyter --paths to see every directory Jupyter searches. If a kernel registered with --user doesn’t appear in JupyterLab, the user data dir is probably not in --paths because JUPYTER_DATA_DIR is set in the environment that started JupyterLab. This is common in conda activate flows that overwrite the variable.

F

FixDevs

Solo developer based in Japan. Every solution is cross-referenced with official documentation and tested before publishing.

Was this article helpful?

Related Articles