QuickAdd User Script for Complex Task Creation - "File Already Exists" Error

Hello Obsidian Community,

I’m working on a QuickAdd user script to automate the creation of a complex task structure in my vault, but I’m encountering a persistent “File already exists” error, despite my efforts to ensure unique filenames.

What I’m Trying to Achieve:

I want to create a script that:

  1. Creates a new task folder in the _31 - TASKS directory.
    • The folder name includes the current date and the task title, formatted as YYYY-MM-DD - Task Title.
  2. Creates a note inside the new folder with the same name as the folder (for folder note compatibility).
    • Applies the Task-Page.md template to this note.
  3. Creates several subfolders within the task folder:
    • _11 - fleeting notes of task
    • _12 - resources notes of task
    • _13 - permanent notes of task
    • __va
  4. Creates index notes (folder notes) within each subfolder using appropriate templates:
    • For example, Fleeting-Board-Page.md for the fleeting notes subfolder.

Example Structure:
If I create a task titled “Write Project Proposal” on 2023-10-15, the script should generate:

_31 - TASKS/
└── 2023-10-15 - Write Project Proposal/
    ├── 2023-10-15 - Write Project Proposal.md  (applies Task-Page.md template)
    ├── _11 - fleeting notes of task/
    │   └── _11 - fleeting notes of task.md  (applies Fleeting-Board-Page.md template)
    ├── _12 - resources notes of task/
    │   └── _12 - resources notes of task.md  (applies Resource-Board-Page.md template)
    ├── _13 - permanent notes of task/
    │   └── _13 - permanent notes of task.md  (applies Permanent-Board-Page.md template)
    └── __va/
        └── __va.md  (applies VA-Board-Page.md template)

The Issue:
When I run my script, I receive the following error:

Note Creation Error: Error: File already exists.
    at t.<anonymous> (app.js:1:732794)
    at app.js:1:237228
    at Object.next (app.js:1:237333)
    at a (app.js:1:236051)

What I’ve Tried:

  • Ensuring Unique Filenames:
    • I attempted to append a number to the task title if a file with the same name already exists, using a loop to check for existing files.
  • Verifying Template Paths:
    • Confirmed that all template files (Task-Page.md, Fleeting-Board-Page.md, etc.) exist in the _90 - META/Templates/ directory.
  • Checking the Script for Errors:
    • Reviewed the script for syntax errors and logical issues.

Despite these efforts, the error persists, and the script does not proceed as expected.

My Script:
Here’s the current version of my script:

// create-task-page-in-global.js

module.exports = async (params) => {
  const { app, quickAddApi } = params;
  const { moment, Notice } = window;
  const { vault, workspace } = app;

  // Define normalizePath function
  function normalizePath(path) {
    return path.replace(/\\/g, "/");
  }

  try {
    // Prompt the user for the task title
    let taskTitle = await quickAddApi.inputPrompt("Enter task title:");
    if (!taskTitle) {
      new Notice("No task title provided.", 3000);
      return;
    }

    // Ensure task title is unique
    const currentDate = moment().format("YYYY-MM-DD");
    let taskFolderName = `${currentDate} - ${taskTitle}`;
    let taskFolderPath = normalizePath(`_31 - TASKS/${taskFolderName}`);
    let taskFileName = `${currentDate} - ${taskTitle}.md`;
    let newNotePath = normalizePath(`${taskFolderPath}/${taskFileName}`);

    let fileCount = 1;
    while (vault.getAbstractFileByPath(newNotePath)) {
      taskTitle = `${taskTitle} (${fileCount})`;
      taskFolderName = `${currentDate} - ${taskTitle}`;
      taskFolderPath = normalizePath(`_31 - TASKS/${taskFolderName}`);
      taskFileName = `${currentDate} - ${taskTitle}.md`;
      newNotePath = normalizePath(`${taskFolderPath}/${taskFileName}`);
      fileCount++;
    }

    // Ensure the target folder exists; if not, create it
    await createFolderIfNotExists(taskFolderPath);

    // Read the content from the Task-Page template
    const templatePath = normalizePath("_90 - META/Templates/Task-Page.md");
    const templateFile = vault.getAbstractFileByPath(templatePath);
    if (!templateFile) {
      new Notice(`Template file "${templatePath}" not found.`, 5000);
      console.error(`Template file "${templatePath}" does not exist.`);
      return;
    }

    // Create subfolders and their notes
    const subfolders = [
      { name: "_11 - fleeting notes of task", template: "Fleeting-Board-Page.md" },
      { name: "_12 - resources notes of task", template: "Resource-Board-Page.md" },
      { name: "_13 - permanent notes of task", template: "Permanent-Board-Page.md" },
      { name: "__va", template: "VA-Board-Page.md" }
    ];

    await Promise.all(subfolders.map(async (subfolder) => {
      const subfolderPath = normalizePath(`${taskFolderPath}/${subfolder.name}`);
      await createFolderIfNotExists(subfolderPath);

      const indexFileName = `${subfolder.name}.md`;
      const indexFilePath = normalizePath(`${subfolderPath}/${indexFileName}`);
      const subTemplatePath = normalizePath(`_90 - META/Templates/${subfolder.template}`);

      const subTemplateFile = vault.getAbstractFileByPath(subTemplatePath);
      if (!subTemplateFile) {
        new Notice(`Template file "${subTemplatePath}" not found.`, 5000);
        console.error(`Template file "${subTemplatePath}" does not exist.`);
        return; // Skip this subfolder if the template is not found
      }

      let subTemplateContent = await vault.read(subTemplateFile);
      subTemplateContent = replacePlaceholders(subTemplateContent, subfolder.name, currentDate);

      await vault.create(indexFilePath, subTemplateContent);
    }));

    // Replace placeholders in the main task template after subfolders are created
    let templateContent = await vault.read(templateFile);
    templateContent = replacePlaceholders(templateContent, taskTitle, currentDate);

    // Create the new note with the template content
    await vault.create(newNotePath, templateContent);

    // Open the newly created main task note
    const file = vault.getAbstractFileByPath(newNotePath);
    if (file) {
      const leaf = workspace.getLeaf(false);
      await leaf.openFile(file);
      new Notice(`Task note "${taskTitle}" created successfully!`, 3000);
    } else {
      new Notice(`Failed to open the new note "${taskTitle}".`, 5000);
      console.error(`Failed to retrieve the newly created file "${newNotePath}".`);
    }

  } catch (error) {
    new Notice(`Error creating task note: ${error.message}`, 5000);
    console.error("Note Creation Error:", error);
  }

  // Helper function to create a folder if it doesn't exist
  async function createFolderIfNotExists(folderPath) {
    try {
      if (!vault.getAbstractFileByPath(folderPath)) {
        await vault.createFolder(folderPath);
      }
    } catch (error) {
      new Notice(`Error creating folder: ${error.message}`, 5000);
      console.error("Folder Creation Error:", error);
    }
  }

  // Helper function to replace placeholders in the template
  function replacePlaceholders(templateContent, title, currentDate) {
    const titlePlaceholder = /<% tp\.file\.title %>/g;
    const datePlaceholder = /<% tp\.date\.now\("YYYY-MM-DD"\) %>/g;
    const cursorPlaceholder = /<% tp\.file\.cursor\(\) %>/g;

    return templateContent
      .replace(titlePlaceholder, title)
      .replace(datePlaceholder, currentDate)
      .replace(cursorPlaceholder, ""); // Remove cursor placeholder for now
  }
};

Templates:

  • Task-Page.md and other templates contain Templater placeholders like <% tp.file.title %>. The script includes a function to replace these placeholders.

Error Message:

Note Creation Error: Error: File already exists.
    at t.<anonymous> (app.js:1:732794)
    at app.js:1:237228
    at Object.next (app.js:1:237333)
    at a (app.js:1:236051)

Request for Help:
I’m unsure why this error occurs, as I’m attempting to ensure that filenames are unique before creating them. Is there something I’m missing in how Obsidian’s vault.create() method works, or is there a better way to handle file creation and uniqueness?

Any insights or suggestions would be greatly appreciated!

Thank you!