EditNotebook tool silently strips required fields from .ipynb files, producing invalid notebooks

Where does the bug appear (feature/product)?

Cursor IDE

Describe the Bug

The EditNotebook tool (used by AI agents to edit Jupyter notebook cells) silently strips required fields from the notebook JSON when it re-serializes the .ipynb file. This happens even to cells and outputs that were not edited — the tool rewrites the entire file and loses data in the process.

Two categories of fields are stripped:

  1. Cell "id" fields (introduced in nbformat v4.5): Every cell in the notebook loses its "id". While older notebooks may not have these, stripping them from notebooks that do is data loss.

  2. Stream output "name" fields (required by nbformat v4 spec): Every stream-type output loses its "name" field (which must be "stdout" or "stderr"). This makes the notebook invalid per the nbformat specification and breaks downstream tools such as Sphinx, myst-nb, and nbconvert that validate notebook structure.

Importance: High

This bug breaks the core workflow of agents editing Jupyter notebooks. Every single EditNotebook invocation silently corrupts the notebook file. Users who rely on agents for iterative notebook editing (a common workflow in data science / ML projects) will find their notebooks fail to render, fail CI checks, or produce broken documentation — with no warning from the tool itself.

Steps to Reproduce

  1. Open any valid .ipynb notebook that has:

    • Cells with "id" fields (standard since nbformat 4.5 / Jupyter Notebook 7+)
    • Code cells with stream outputs containing a "name" field (e.g. any cell that prints output)
  2. Ask the AI agent to make a trivial text edit to a markdown cell using EditNotebook (e.g. fix a typo).

  3. Inspect the saved .ipynb file:

import json

with open("your_notebook.ipynb") as f:
    nb = json.load(f)

# Check cell ids
has_id = sum(1 for c in nb["cells"] if "id" in c)
print(f"Cells with 'id': {has_id} / {len(nb['cells'])}")

# Check stream output 'name' fields
for i, cell in enumerate(nb["cells"]):
    for j, output in enumerate(cell.get("outputs", [])):
        if output.get("output_type") == "stream" and "name" not in output:
            print(f"Cell {i}, output {j}: stream output MISSING required 'name' field")

Expected Behavior

  • EditNotebook should only modify the cell content that was actually edited.
  • All other fields in the notebook JSON — including cell "id" fields, output "name" fields, and any other metadata — should be preserved exactly as they were.
  • The tool should never produce a notebook that violates the nbformat specification.

Actual Behavior

  • All cell "id" fields are stripped (not just the edited cell’s).
  • All stream output "name" fields are stripped (not just outputs in the edited cell).
  • The tool reports success with no warning that fields were dropped.
  • The resulting notebook is invalid per nbformat v4 and breaks Sphinx/myst-nb doc builds, nbconvert, and other tools that validate notebook structure.

Operating System

Windows 10/11
MacOS

Version Information

  • OS: macOS 26.2 (arm64)
  • Cursor Version: 2.4.28
  • Commit: f3f5cec40024283013878b50c4f9be4002e0b580
  • Architecture: arm64

For AI issues: which model did you use?

Claude-4.6-opus-high

Additional Information

Evidence from git history

We observed this across multiple EditNotebook invocations on the same notebook:

State Cells with "id" Stream outputs with "name"
Before EditNotebook 44 / 44 6 / 6
After EditNotebook 0 / 44 0 / 6

The fields are stripped from every cell and output in the notebook, not just the ones that were edited.

Does this stop you from using Cursor

No - Cursor works, but with this issue

Hey, thanks for the detailed bug report. It’s really well documented.

This is a known issue with the EditNotebook tool. The team is aware of it, especially the part about losing cell IDs. Your report helps fill in the gaps, and the part about losing the "name" fields in stream outputs is a key detail for nbformat compliance.

I shared this thread with the team to help with prioritization. Unfortunately, we don’t have an ETA yet.

This topic was automatically closed 22 days after the last reply. New replies are no longer allowed.