-
Notifications
You must be signed in to change notification settings - Fork 15
Description
Bug description
The moveTasks
function only successfully moves the first task when multiple task IDs are provided in the ids
array. Subsequent tasks in the array are not moved to the target destination (project, section, or parent task), despite the API call appearing to succeed.
Expected behaviour
When calling moveTasks(['task1', 'task2', 'task3'], { sectionId: 'target-section' })
, all three tasks should be moved to the target section.
Is reproducible
Yes
To reproduce
- Create multiple tasks in a Todoist account
- Call the
moveTasks
function with an array containing 2 or more task IDs - Provide move arguments (e.g.,
{ sectionId: 'target-section-id' }
) - Check the target destination after the API call completes
- Observe that only the first task in the array has been moved
Minimal reproduction script:
const { TodoistApi } = require('@doist/todoist-api-typescript');
async function reproduceMoveBug() {
const api = new TodoistApi('YOUR_API_TOKEN');
// Replace with actual task IDs and section ID from your account
const taskIds = ['TASK_ID_1', 'TASK_ID_2'];
const targetSectionId = 'TARGET_SECTION_ID';
console.log(`Moving ${taskIds.length} tasks to section: ${targetSectionId}`);
// Call moveTasks - this will appear to succeed
const movedTasks = await api.moveTasks(taskIds, { sectionId: targetSectionId });
console.log(`API returned ${movedTasks.length} moved tasks`);
// Verify by checking what's actually in the target section
const tasksInSection = await api.getTasks({ sectionId: targetSectionId });
const foundTaskIds = tasksInSection.results
.filter(task => taskIds.includes(task.id))
.map(task => task.id);
console.log(`Expected to find: ${taskIds.length} tasks`);
console.log(`Actually found: ${foundTaskIds.length} tasks`);
console.log(`Found task IDs: [${foundTaskIds.join(', ')}]`);
if (foundTaskIds.length < taskIds.length) {
console.log('❌ BUG CONFIRMED: Not all tasks were moved!');
const missingTasks = taskIds.filter(id => !foundTaskIds.includes(id));
console.log(`Missing tasks: [${missingTasks.join(', ')}]`);
} else {
console.log('✅ All tasks moved successfully');
}
}
reproduceMoveBug().catch(console.error);
Expected vs Actual behavior:
- Expected: Both tasks appear in the target section
- Actual: Only the first task (
TASK_ID_1
) appears in the target section
Steps taken to try to reproduce
- Created integration test script that calls
moveTasks
with multiple task IDs - Verified tasks exist and are accessible via the API
- Confirmed API call returns successfully with all expected task objects
- Queried target section after move operation to verify actual task locations
- Tested with different move targets (projects, sections, parent tasks)
Screenshots
Not applicable - this is an API behavior issue.
Version information:
- Package version: 5.1.1
Additional information
Root Cause: The issue is in the moveTasks
function at line 312-315 in src/TodoistApi.ts
. The function generates a single UUID and reuses it for all commands:
const uuid = uuidv4() // Single UUID generated once
const commands: Command[] = ids.map((id) => ({
type: 'item_move',
uuid, // Same UUID reused for all commands
args: { ... }
}))
According to the Todoist sync API specification, each command requires a unique UUID. When the same UUID is used for multiple commands, the API treats them as duplicates and only processes the first one.
Fix: Generate a unique UUID for each command:
const commands: Command[] = ids.map((id) => ({
type: 'item_move',
uuid: uuidv4(), // Unique UUID for each command
args: { ... }
}))
Testing: This issue can be verified with an integration test that:
- Calls
moveTasks
with multiple task IDs - Queries the target destination to confirm all tasks were actually moved
- Unit tests alone cannot catch this bug as they mock the API responses
The fix has been tested and confirmed to resolve the issue with all tasks being successfully moved when multiple IDs are provided.