Skip to content

Deleting a label then saving fails: the label reappears upon reloading #221

Description

@thccheung

Pre-submission checklist

  • I am using the latest released version of napari-deeplabcut. If not, I have provided minimal installation information, e.g. via the debug log.
  • I have searched existing issues to avoid duplicates

Bug summary

Once a label has been placed and saved, deleting the label (e.g., if the keypoint is not in the frame) deletes it from the napari GUI, but saving it does nothing (no "overwrite" warning). Upon reloading, the label reappears.

Steps to reproduce

  1. Open napari.
  2. Label a keypoint (or several keypoints), then save.
  3. Exit napari.
  4. Re-open napri, load the same image.
  5. Delete a label (or several labels), then save. (Note: no "overwrite" warning, although there is a "Last saved at..." text in bottom right corner.)
  6. Exit napari.
  7. Re-open napri, load the same image.
  8. The label is still there, it has not been deleted.

Expected behavior

  1. When the label has been deleted, saving the selected layer should trigger an "overwrite" warning (if enabled).
  2. When reloading the same image, the deleted label should not reappear (and should remain deleted instead).
  3. The x and y coordinates of that label in the .h5 and .csv file should be blank or "None" (I think - I didn't dig into the .h5 file).

Actual behavior

The previously deleted label is still there when the image is reopened in napari.

Data type

Single-animal

How are you running napari?

From within the DeepLabCut GUI

Additional context

If I open the same folder/image with a previous version of napari-deeplabcut (napari-deeplabcut 0.2.1.7, via DeepLabCut 3.0.0rc13), I can use that napari-deeplabcut to delete the same label and then save it, and the label will remain deleted upon reloading the file. So the bug seems to be associated with the newer napari-deeplabcut version.

Debug log (highly recommended)

Environment

  • python: 3.12.13 | packaged by conda-forge | (main, Mar 5 2026, 16:36:12) [MSC v.1944 64 bit (AMD64)]
  • platform: Windows-11-10.0.26200-SP0
  • napari-deeplabcut: 0.3.1.0
  • napari: 0.6.6
  • qtpy: 2.4.3
  • PySide6: 6.11.1
  • PyQt6: not-installed
  • shiboken6: 6.11.1
  • numpy: 1.26.4
  • pydantic: 2.14.0a1
  • plugin_module_path: C:\ProgramData\Anaconda3\envs\DEEPLABCUT_3.0\Lib\site-packages\napari_deeplabcut_init_.py

Viewer

  • n_layers: 2
  • active_layer: CollectedData_tc

Layers

  • [1] name='images', type=Image, visible=True
    • data_shape: (2, 492, 656, 3)
    • metadata_keys: ['dlc', 'paths', 'project', 'root']
    • property_keys: []
    • project: DLC_Projects/troubleshoot-tc-2026-06-02
    • root: labeled-data/A2ACr597_Pre-S1-1
    • paths_count: 2
    • has_header: False
    • has_tables: False
    • has_save_target: False
  • [2] name='CollectedData_tc', type=Points, visible=True
    • data_shape: (1, 3)
    • metadata_keys: ['colormap_name', 'config_colormap', 'face_color_cycles', 'header', 'io', 'kind', 'name', 'paths', 'root', 'shape', 'source_h5', 'source_h5_name', 'source_h5_stem']
    • property_keys: ['id', 'label', 'likelihood', 'valid']
    • project: None
    • root: labeled-data/A2ACr597_Pre-S1-1
    • paths_count: 2
    • has_header: True
    • has_tables: False
    • has_save_target: False

Recent plugin logs

21:05:46.486 | DEBUG    | napari_deeplabcut.ui.plots.trajectory | Loading trajectory plot DataFrame from layer: None
21:05:46.591 | DEBUG    | napari_deeplabcut.ui.plots.trajectory | Loading trajectory plot DataFrame from layer: None
21:05:46.601 | DEBUG    | napari_deeplabcut.core.layer_lifecycle.manager | Lifecycle manager adopting existing layers count=0
21:05:46.905 | DEBUG    | napari_deeplabcut.core.io | READ_HDF file=F:\DLC_Projects\troubleshoot-tc-2026-06-02\labeled-data\A2ACr597_Pre-S1-1\CollectedData_tc.h5
21:05:46.905 | DEBUG    | napari_deeplabcut.core.io | READ_HDF raw column names=['bodyparts', 'coords']
21:05:46.905 | DEBUG    | napari_deeplabcut.core.io | READ_HDF raw first columns=[('body', 'x'), ('body', 'y')]
21:05:46.905 | DEBUG    | napari_deeplabcut.core.io | READ_HDF header.bodyparts=['body']
21:05:46.921 | DEBUG    | napari_deeplabcut.core.io | STACKED df bodyparts unique=['body']
21:05:46.921 | DEBUG    | napari_deeplabcut.core.io | STACKED df individuals unique=['']
21:05:47.375 | DEBUG    | napari_deeplabcut.core.layer_lifecycle.manager | Lifecycle manager processing insert layer='images' type=Image index=0
21:05:47.375 | DEBUG    | napari_deeplabcut.core.layer_lifecycle.manager | Setup image layer='images' index=0 reorder=True paths_count=2 root='F:\\DLC_Projects\\troubleshoot-tc-2026-06-02\\labeled-data\\A2ACr597_Pre-S1-1' context_changed=True
21:05:47.387 | DEBUG    | napari_deeplabcut.ui.plots.trajectory | Loading trajectory plot DataFrame from layer: None
21:05:47.444 | DEBUG    | napari_deeplabcut.ui.plots.trajectory | Loading trajectory plot DataFrame from layer: None
21:05:47.818 | DEBUG    | napari_deeplabcut.core.layer_lifecycle.manager | Lifecycle manager processing insert layer='CollectedData_tc' type=Points index=1
21:05:47.841 | DEBUG    | napari_deeplabcut._widgets | Setup points layer='CollectedData_tc' metadata_keys=['colormap_name', 'config_colormap', 'face_color_cycles', 'header', 'io', 'kind', 'name', 'paths', 'root', 'source_h5', 'source_h5_name', 'source_h5_stem']
21:05:47.841 | DEBUG    | napari_deeplabcut.core.layer_lifecycle.manager | Wire points layer='CollectedData_tc' existing_store=True project=None root=F:\DLC_Projects\troubleshoot-tc-2026-06-02\labeled-data\A2ACr597_Pre-S1-1 len_paths=2
21:05:47.841 | DEBUG    | napari_deeplabcut.core.layer_lifecycle.manager | Setup points layer='CollectedData_tc' allow_merge=True metadata_keys=['colormap_name', 'config_colormap', 'face_color_cycles', 'header', 'io', 'kind', 'name', 'paths', 'root', 'source_h5', 'source_h5_name', 'source_h5_stem']
21:05:47.841 | DEBUG    | napari_deeplabcut.core.layer_lifecycle.manager | Remap start layer='CollectedData_tc' old_paths_len=2 new_paths_len=2 data_shape=(2, 3) frame_min=0 frame_max=1
21:05:47.841 | DEBUG    | napari_deeplabcut.core.remap | Remap mapping stats: depth=3 old=2 new=2 mapped=2 identity=2 overlap=2
21:05:47.841 | DEBUG    | napari_deeplabcut.core.layer_lifecycle.manager | Remap result layer='CollectedData_tc' changed=False mapped_count=2 depth=3 message=Path keys already aligned; no remap needed. warnings=()
21:05:47.841 | DEBUG    | napari_deeplabcut.core.layer_lifecycle.manager | Remap accepted-noop for CollectedData_tc (depth=3, mapped=2): Path keys already aligned; no remap needed.
21:05:47.842 | DEBUG    | napari_deeplabcut.ui.plots.trajectory | Loading trajectory plot DataFrame from layer: <Points layer 'CollectedData_tc' at 0x1d84dbb9a30>
21:05:47.933 | DEBUG    | napari_deeplabcut.core.dataframes | Before reindex: cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:05:47.934 | DEBUG    | napari_deeplabcut.core.dataframes | header cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:05:47.934 | DEBUG    | napari_deeplabcut.core.dataframes | After reindex: cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:05:47.936 | DEBUG    | napari_deeplabcut.core.dataframes | header cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:05:47.939 | DEBUG    | napari_deeplabcut.ui.plots.trajectory | Trajectory plot built: layer='CollectedData_tc' df_shape=(2, 2) n_series=1 frame_bounds=(0.0, 1.0)
21:05:47.956 | DEBUG    | napari_deeplabcut.ui.plots.trajectory | Determining visible trajectory lines from points layer: <Points layer 'CollectedData_tc' at 0x1d84dbb9a30>
21:05:47.956 | DEBUG    | napari_deeplabcut.ui.color_scheme_display | Skipping unavailable Points event 'shown' for layer='CollectedData_tc'
21:05:47.980 | DEBUG    | napari_deeplabcut.ui.plots.trajectory | Loading trajectory plot DataFrame from layer: <Points layer 'CollectedData_tc' at 0x1d84dbb9a30>
21:05:47.988 | DEBUG    | napari_deeplabcut.core.dataframes | Before reindex: cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:05:47.988 | DEBUG    | napari_deeplabcut.core.dataframes | header cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:05:47.989 | DEBUG    | napari_deeplabcut.core.dataframes | After reindex: cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:05:47.991 | DEBUG    | napari_deeplabcut.core.dataframes | header cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:05:47.993 | DEBUG    | napari_deeplabcut.ui.plots.trajectory | Trajectory plot built: layer='CollectedData_tc' df_shape=(2, 2) n_series=1 frame_bounds=(0.0, 1.0)
21:05:48.011 | DEBUG    | napari_deeplabcut.ui.plots.trajectory | Determining visible trajectory lines from points layer: <Points layer 'CollectedData_tc' at 0x1d84dbb9a30>
21:05:48.017 | DEBUG    | napari_deeplabcut.ui.plots.trajectory | Loading trajectory plot DataFrame from layer: <Points layer 'CollectedData_tc' at 0x1d84dbb9a30>
21:05:48.024 | DEBUG    | napari_deeplabcut.core.dataframes | Before reindex: cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:05:48.024 | DEBUG    | napari_deeplabcut.core.dataframes | header cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:05:48.025 | DEBUG    | napari_deeplabcut.core.dataframes | After reindex: cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:05:48.027 | DEBUG    | napari_deeplabcut.core.dataframes | header cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:05:48.029 | DEBUG    | napari_deeplabcut.ui.plots.trajectory | Trajectory plot built: layer='CollectedData_tc' df_shape=(2, 2) n_series=1 frame_bounds=(0.0, 1.0)
21:05:48.045 | DEBUG    | napari_deeplabcut.ui.plots.trajectory | Determining visible trajectory lines from points layer: <Points layer 'CollectedData_tc' at 0x1d84dbb9a30>
21:05:48.049 | DEBUG    | napari_deeplabcut.ui.plots.trajectory | Loading trajectory plot DataFrame from layer: <Points layer 'CollectedData_tc' at 0x1d84dbb9a30>
21:05:48.056 | DEBUG    | napari_deeplabcut.core.dataframes | Before reindex: cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:05:48.056 | DEBUG    | napari_deeplabcut.core.dataframes | header cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:05:48.057 | DEBUG    | napari_deeplabcut.core.dataframes | After reindex: cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:05:48.059 | DEBUG    | napari_deeplabcut.core.dataframes | header cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:05:48.061 | DEBUG    | napari_deeplabcut.ui.plots.trajectory | Trajectory plot built: layer='CollectedData_tc' df_shape=(2, 2) n_series=1 frame_bounds=(0.0, 1.0)
21:05:48.078 | DEBUG    | napari_deeplabcut.ui.plots.trajectory | Determining visible trajectory lines from points layer: <Points layer 'CollectedData_tc' at 0x1d84dbb9a30>
21:05:48.093 | DEBUG    | napari_deeplabcut.ui.color_scheme_display | Updating color scheme display with 1 entries
21:06:02.902 | DEBUG    | napari_deeplabcut.core.metadata | Raw header type=<class 'dict'>
21:06:02.918 | DEBUG    | napari_deeplabcut.core.metadata | Raw header has columns=False
21:06:02.918 | DEBUG    | napari_deeplabcut.core.metadata | columns type=<class 'NoneType'>
21:06:02.918 | DEBUG    | napari_deeplabcut._widgets | About to save. io.kind=<AnnotationKind.GT: 'gt'> save_target=None
21:06:02.923 | DEBUG    | napari_deeplabcut.core.metadata | Raw header type=<class 'dict'>
21:06:02.923 | DEBUG    | napari_deeplabcut.core.metadata | Raw header has columns=False
21:06:02.923 | DEBUG    | napari_deeplabcut.core.metadata | columns type=<class 'NoneType'>
21:06:02.934 | DEBUG    | napari_deeplabcut.core.dataframes | Before reindex: cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:06:02.934 | DEBUG    | napari_deeplabcut.core.dataframes | header cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:06:02.934 | DEBUG    | napari_deeplabcut.core.dataframes | After reindex: cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:06:02.949 | DEBUG    | napari_deeplabcut.core.dataframes | header cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:06:02.949 | DEBUG    | napari_deeplabcut.core.metadata | Raw header type=<class 'dict'>
21:06:02.949 | DEBUG    | napari_deeplabcut.core.metadata | Raw header has columns=False
21:06:02.949 | DEBUG    | napari_deeplabcut.core.metadata | columns type=<class 'NoneType'>
21:06:02.987 | DEBUG    | napari_deeplabcut.core.metadata | Raw header type=<class 'dict'>
21:06:02.987 | DEBUG    | napari_deeplabcut.core.metadata | Raw header has columns=False
21:06:02.987 | DEBUG    | napari_deeplabcut.core.metadata | columns type=<class 'NoneType'>
21:06:02.987 | DEBUG    | napari_deeplabcut.core.io | HEADER nlevels: 4
21:06:02.987 | DEBUG    | napari_deeplabcut.core.io | HEADER names: ['scorer', 'individuals', 'bodyparts', 'coords']
21:06:02.987 | DEBUG    | napari_deeplabcut.core.io | WRITE header bodyparts=['body']
21:06:02.987 | DEBUG    | napari_deeplabcut.core.io | WRITE props labels unique=['body']
21:06:02.987 | DEBUG    | napari_deeplabcut.core.dataframes | Before reindex: cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:06:02.987 | DEBUG    | napari_deeplabcut.core.dataframes | header cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:06:02.998 | DEBUG    | napari_deeplabcut.core.dataframes | After reindex: cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:06:02.998 | DEBUG    | napari_deeplabcut.core.dataframes | header cols nlevels 4, names ['scorer', 'individuals', 'bodyparts', 'coords']
21:06:02.998 | DEBUG    | napari_deeplabcut.core.io | DF_NEW columns nlevels: 4
21:06:02.998 | DEBUG    | napari_deeplabcut.core.io | DF_NEW columns names: ['scorer', 'individuals', 'bodyparts', 'coords']
21:06:03.002 | DEBUG    | napari_deeplabcut.core.io | DF_NEW finite count: 2
21:06:03.002 | DEBUG    | napari_deeplabcut.core.metadata | Raw header type=<class 'dict'>
21:06:03.002 | DEBUG    | napari_deeplabcut.core.metadata | Raw header has columns=False
21:06:03.002 | DEBUG    | napari_deeplabcut.core.metadata | columns type=<class 'NoneType'>
21:06:03.014 | DEBUG    | napari_deeplabcut.core.io | Resolved multianimalproject=False from config at F:\DLC_Projects\troubleshoot-tc-2026-06-02\config.yaml
21:06:03.014 | DEBUG    | napari_deeplabcut.core.io | FINAL WRITE first columns=[('tc', 'body', 'x'), ('tc', 'body', 'y')]
21:06:03.014 | DEBUG    | napari_deeplabcut.core.io | FINAL WRITE bodyparts unique=['body']
21:06:03.014 | DEBUG    | napari_deeplabcut.core.io | FINAL WRITE columns nlevels: 3
21:06:03.014 | DEBUG    | napari_deeplabcut.core.io | FINAL WRITE columns names: ['scorer', 'bodyparts', 'coords']
21:06:03.045 | DEBUG    | napari_deeplabcut.core.metadata | Raw header type=<class 'dict'>
21:06:03.045 | DEBUG    | napari_deeplabcut.core.metadata | Raw header has columns=False
21:06:03.045 | DEBUG    | napari_deeplabcut.core.metadata | columns type=<class 'NoneType'>

config.yaml

Metadata

Metadata

Assignees

Labels

bugSomething isn't working

Type

Fields

No fields configured for Bug.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions