#1648·jupytext

Paired notebooks + jupyter-collaboration: saving one file silently discards unsaved edits in its twin

Author: gtherinCreated Sep 18, 2026Updated Sep 21, 2026
Labelsjupyter-collaboration

Summary

  1. A paired notebook is two files, demo.ipynb and demo.py. With jupyter-collaboration, each open file also has a live document in memory.
  2. Someone edits demo.py in one tab while demo.ipynb is open in another.
  3. Saving the notebook makes Jupytext rewrite demo.py on disk, as designed.
  4. jupyter-collaboration sees the file change, trusts the disk, and replaces the open demo.py document. Everything typed there since its last save is lost, with no dialog.
  5. It happens at every save, autosave included, in both directions. The next save from the overwritten editor is refused as OutOfBandChanges, which is the "File Changed" dialog of #1447.

Neither package is wrong on its own; each assumes it is the only writer.

Impact

On a JupyterHub used for a hackathon (2026-09-14), participants lost work repeatedly. We counted 431 Out-of-band changes. Overwriting the content in room events that day; for three of the four participants behind almost all of them, 97–100 % followed a save of another document in the same server within 3 s (73 % for the fourth; chance level about 16 %), almost always notebook saved, then .py overwritten. We removed pairing from all course material, but cannot rule it out server-side (proposal 2).

Reproduction

  1. pip install jupyterlab jupytext jupyter-collaboration
  2. Pair demo.ipynb with formats: ipynb,py:percent.
  3. Open demo.ipynb as a notebook and demo.py in the text editor.
  4. Type in demo.py without saving, then edit and save demo.ipynb.
  5. The unsaved text in demo.py is replaced; saving demo.py raises "File Changed".

Root cause

Jupytext writes the twin through the contents manager (async_pairs.write_pairsave_one_fileself.super.save), with force_update_timestamp=True, so the twin's mtime always moves. jupyter-server-ydoc only trusts timestamps it recorded itself: FileLoader.maybe_notify sees a newer last_modified, every subscribed room reloads from disk (rooms.py), and maybe_save_content then raises OutOfBandChanges. A write made by the server itself is indistinguishable from an external edit.

Proposals

  1. Do not write a twin behind an open room. When the twin has a live room, push the new content into its shared document (keeps unsaved edits), or at least update the room's recorded last_modified (removes the false signal). Either would close #1447.
  2. A switch to ignore pairing declared in notebook metadata, e.g. JupytextConfiguration.allow_notebook_formats_metadata = False (default True). Today notebook_formats() always honours a notebook's own jupytext.formats, so a hub operator cannot guarantee pairing is off.
  3. Warn when a paired save happens with jupyter_server_ydoc enabled, and document the limitation. We had no signal until work was lost.

Versions

jupytext 1.19.5 · jupyterlab 4.6.3 · jupyter-collaboration 5.0.2 · jupyter-server-ydoc 3.0.2 · jupyter-ydoc 4.1.1 · pycrdt 0.14.4 · Python 3.12

We can test candidate fixes on a live multi-user hub and contribute a reproduction test next to the collaboration load tests of #1552. Possibly related: #1501.