HumanSignal / HumanSignal/label-studio

VideoRectangle regions are duplicated as phantom timelinelabels results in exported JSON when <TimelineLabels> and <VideoRectangle> share the same <Video>

Open
#9,744 0 comments 0 reactions 0 assignees View on GitHub
Dominant language
TypeScript
Stars
28.3k
Forks
3.7k
Avg merge
14h
Merged PRs (30d)
15

Description

### Describe the bug

When a labeling config attaches **both** `` and `` to the same `` object (a very common setup for "label the time range + draw a bbox on a key frame"), every `VideoRectangle` region is **silently duplicated** into the exported JSON as a phantom `timelinelabels` result.

The duplicated record:
- shares the **exact same `id`** as the real `videorectangle` record;
- has `from_name` rewritten to the TimelineLabels control name;
- has `type: "timelinelabels"`;
- copies the bbox `sequence` / `framesCount` / `duration` verbatim into `value`;
- but is **missing the `ranges` field** that a real TimelineLabels region must have.

This also makes the same region appear twice in the **Regions** side panel inside the UI, which confuses annotators.

### To Reproduce

1. Create a project with the following labeling config:

```xml






```

2. Open a task, create **one** TimelineLabels range (e.g. frames 20–53) with label `motion_inconsistency`.
3. Create **one** VideoRectangle bbox at frame 53.
4. (Optional) Connect them with a relation. The bug reproduces with or without the relation.
5. Submit the annotation and export the project as JSON.

### Expected behavior

The exported `result` array should contain **exactly two** records:
- one `timelinelabels` record with `value.ranges`,
- one `videorectangle` record with `value.sequence`.

### Actual behavior

The exported `result` contains **three** records. The third one is a phantom copy of the bbox, mis-typed as `timelinelabels`:

```json
[
{
"id": "xYs3la2xdd",
"from_name": "frame_range",
"to_name": "video",
"type": "timelinelabels",
"value": {
"ranges": [{ "start": 20, "end": 53 }],
"timelinelabels": ["motion_inconsistency"]
}
},
{
"id": "ZtqF77pQvX",
"from_name": "bbox",
"to_name": "video",
"type": "videorectangle",
"value": {
"framesCount": 2001,
"duration": 66.688,
"sequence": [
{ "frame": 53, "enabled": true, "rotation": 0,
"x": 34.296875, "y": 10.833333, "width": 27.46875, "height": 58.833333,
"time": 1.766666 }
],
"labels": ["motion_inconsistency"]
}
},

// 👇 PHANTOM RECORD — same id as the bbox above, but mis-typed
{
"id": "ZtqF77pQvX",
"from_name": "frame_range",
"to_name": "video",
"type": "timelinelabels",
"value": {
"framesCount": 2001,
"duration": 66.688,
"sequence": [
{ "frame": 53, "enabled": true, "rotation": 0,
"x": 34.296875, "y": 10.833333, "width": 27.46875, "height": 58.833333,
"time": 1.766666 }
],
"timelinelabels": ["motion_inconsistency"]
}
}
]
```

Note the fingerprints of the phantom record:
- same `id` as a real `videorectangle` record;
- `from_name == "frame_range"` (the TimelineLabels control), `type == "timelinelabels"`;
- has `value.sequence` (which a real TimelineLabels region never has);
- **does not** have `value.ranges` (which a real TimelineLabels region always has).

These four conditions together can be used to filter the phantom records out, and they have never produced false positives in our data.

### Environment

- Label Studio version: **1.23.0** (also reproduced on the latest tag at the time of writing)
- Deployment: self-hosted (Docker / pip — both reproduce)
- Browser: Chrome 130, Firefox 128 (both reproduce)

Contributor guide

Open the contributing guide

Assessment

This issue has not been assessed yet.

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.