2025, Nov 22 21:00

How to stop PEP8 E303 false positives in JupyterLab after Markdown cells (pylsp, flake8, Windows 10)

Fix PEP8 E303 'too many blank lines' in JupyterLab on Windows 10. Learn why pylsp/flake8 flag cells after Markdown and how to suppress it via Code Diagnostics.

When you wire up a PEP8 linter in JupyterLab on Windows 10 via pylsp and start getting an “E303 too many blank lines” warning at the first line of every code cell that follows a Markdown cell, it feels like the tooling is gaslighting you. The code is fine, yet the editor insists otherwise. This guide explains why it happens and how to suppress the false positives without breaking your linting workflow.

Reproducing the annoyance

The issue shows up regardless of what code runs in the cell after a Markdown cell. Even a tiny, PEP8-compliant snippet will trigger it:

def sum_pair(x_val, y_val):
    return x_val + y_val
res_total = sum_pair(1, 2)
print(res_total)

Place this cell right after a Markdown cell and watch the linter mark the very first line with E303, even though the cell itself doesn’t start with blank lines.

What’s actually going on

The behavior is tracked in the jupyterlab-lsp repository and appears linked to a more general JupyterLab issue related to added spaces around Markdown cells. You can read the discussions here: jupyterlab-lsp issue and JupyterLab issue. The key point is that this is not a flake8 problem. The UI ends up seeing extra whitespace around Markdown boundaries, and the diagnostic gets raised as if your code cell began with extra blank lines.

The practical fix right now

Since the upstream issues are still open, the pragmatic workaround is to ignore E303 at the UI level in JupyterLab. First, remove any E303 entry you might have added under pylsp.plugins.flake8.ignore. Because the root cause isn’t in flake8, silencing it there may not have any effect, especially if your environment isn’t actually using flake8 or if flake8 is disabled.

Then, open JupyterLab and navigate to Settings > Code Diagnostics > Diagnostics code to ignore. Add E303 there. This suppresses the false positive in the interface, regardless of which specific linter plugin is producing the diagnostic.

Why ignoring in the UI works when flake8.ignore doesn’t

There is an important distinction between ignoring diagnostics at different layers. The pylsp.plugins.flake8.ignore setting asks flake8, via the Language Server, to filter certain codes before they reach the editor. That only affects diagnostics coming from flake8. The Code Diagnostics “Diagnostics code to ignore” setting tells the JupyterLab UI to drop those codes after they arrive from the LSP, which means it covers diagnostics uniformly, whether they originate from flake8, pycodestyle, pyflakes, ruff, or anything else routed through the language server.

Do you need to change your code?

No code changes are necessary, because the problem is not in your source. The snippet below remains perfectly valid and will lint cleanly once you apply the UI-level ignore for E303:

def sum_pair(x_val, y_val):
    return x_val + y_val
res_total = sum_pair(1, 2)
print(res_total)

Why this matters

False positives erode trust in static analysis tools and distract from real issues. Until the linked issues in JupyterLab and jupyterlab-lsp are resolved, handling E303 at the UI level keeps your notebook editing experience focused and consistent without turning off linters entirely.

Takeaways

If you see E303 flagged on the first line of code cells following Markdown, it’s a known interaction between JupyterLab and the linter pipeline. Don’t fight your code. Remove E303 from pylsp.plugins.flake8.ignore, and add E303 under Settings > Code Diagnostics > Diagnostics code to ignore. That workaround has been confirmed to resolve the noise while the upstream issues remain open.