Skip to content

Instantly share code, notes, and snippets.

Show Gist options
  • Select an option

  • Save dangnhdev/6ad506203076a6ff0a50870b9ae025d7 to your computer and use it in GitHub Desktop.

Select an option

Save dangnhdev/6ad506203076a6ff0a50870b9ae025d7 to your computer and use it in GitHub Desktop.
Vikunja TickTick import bug: child row before parent row can fail with task title cannot be empty

Vikunja TickTick import: child row before parent row

AI assistance note: I used AI to explore the Vikunja repository, trace the TickTick import code path, reorder/fix the CSV, and verify that the reordered CSV imported successfully.

Minimal reproduction CSV

The attached file vikunja-ticktick-parent-before-child-minimal.csv reproduces the ordering problem:

  • the child task row comes first
  • parentId points to a parent task row that appears later in the file

Relevant code paths

  • TickTick importer parses parentId and turns it into a parenttask relation in pkg/modules/migration/ticktick/ticktick.go
  • Structure insertion creates tasks in CSV row order in pkg/modules/migration/create_from_structure.go
  • If a related task does not exist yet, the relation code tries to create it on the fly in pkg/modules/migration/create_from_structure.go
  • Task creation rejects empty titles in pkg/models/tasks.go
  • The returned error text is Task title cannot be empty. from pkg/models/error.go

Why the bug happens

The TickTick importer builds parent-child relations from parentId, but the later insert step still creates tasks sequentially in the same order they appear in the CSV.

When a child row appears before its parent row:

  1. the child task is created first
  2. Vikunja sees a parenttask relation to the parent ID
  3. the parent task has not been created yet
  4. the relation code tries to create a placeholder related task using only the referenced ID
  5. that placeholder has no title
  6. task validation fails with Task title cannot be empty.

So the import becomes sensitive to CSV row order, even though parent-child references should be resolvable independently of row position.

What I observed

Using a real TickTick export, I found multiple cases where a child row appeared before its parent row. After reordering the CSV so that every parent row appeared before all of its children, the import completed successfully.

Workaround used

  • keep the TickTick export format intact
  • reorder rows so every parent task appears before its child tasks
  • optionally remove rows with empty Title values if they also exist in the export

Suggested upstream fix

Create all tasks first, build a complete old-ID to new-ID map, and only create task relations in a second pass after every task already exists.

That would make TickTick import independent of CSV row order and avoid creating placeholder tasks without titles.

We can make this file beautiful and searchable if this error is corrected: It looks like row 4 should actually have 1 column, instead of 24 in line 3.
"Date: 2026-03-26+0000"
"Version: 7.1"
"Status:
0 Normal
1 Completed
2 Archived"
"Folder Name","List Name","Title","Kind","Tags","Content","Is Check list","Start Date","Due Date","Reminder","Repeat","Priority","Status","Created Time","Completed Time","Order","Timezone","Is All Day","Is Floating","Column Name","Column Order","View Mode","taskId","parentId"
"All","Personal","Child task appears first","TEXT","","","N","","","","","0","0","2026-03-26T10:00:00+0000","","-1","UTC","false","false","Not Sectioned","-1","list","1002","1001"
"All","Personal","Parent task appears later","TEXT","","","N","","","","","0","0","2026-03-26T10:00:01+0000","","-2","UTC","false","false","Not Sectioned","-1","list","1001",""
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment