Skip to content

fix: prevent Templater double-execution race with Modal Forms#1090

Open
chhoumann wants to merge 1 commit intomasterfrom
1084-bug-modal-forms-breaking-with-quickadd-1
Open

fix: prevent Templater double-execution race with Modal Forms#1090
chhoumann wants to merge 1 commit intomasterfrom
1084-bug-modal-forms-breaking-with-quickadd-1

Conversation

@chhoumann
Copy link
Owner

@chhoumann chhoumann commented Jan 30, 2026

Summary

Fixes #1084 - Modal Forms dialogs appearing twice when using QuickAdd templates.

Root Cause Analysis

The bug was a race condition between two independent Templater execution paths:

  1. Templater's auto-trigger (trigger_on_file_creation setting)
  2. QuickAdd's explicit call (overwriteTemplaterOnce())

The Problematic Flow (Before)

1. QuickAdd adds file path to Templater's `files_with_pending_templates` (suppression)
2. QuickAdd creates file via `vault.create()`
3. Templater's trigger-on-create event fires, starts ~300ms countdown
4. After 350ms, QuickAdd REMOVES path from `files_with_pending_templates`
5. QuickAdd calls `overwriteTemplaterOnce()` explicitly
6. If Templater's countdown finishes AFTER step 4, it sees "not pending" and proceeds
7. BOTH auto-trigger AND explicit call run → Modal Forms opens twice

The core issue: suppression ended before the explicit overwrite call, creating a window where Templater's auto-trigger could sneak through.

Why Timing-Based Fixes Don't Work

  • Increasing the suppression buffer (350ms → 1500ms) only reduces frequency, never eliminates the race
  • Any change in Obsidian event ordering, Templater delays, or async scheduling can re-break it
  • Users pay UX latency (every create waits N ms) and still might race

The Fix: Mutual Exclusion

Pick ONE runner, not both. For newly created files:

trigger_on_file_creation Suppression Runner
Enabled ❌ No (let auto-trigger fire) waitForTemplaterTriggerOnCreateToComplete()
Disabled ✅ Yes overwriteTemplaterOnce()

This eliminates the entire class of "auto-trigger starts late" races by ensuring exactly one of {auto-trigger, explicit overwrite} runs.

The New Flow (After)

When trigger_on_file_creation is enabled:

1. QuickAdd creates file (NO suppression)
2. Templater's auto-trigger fires and processes the template
3. QuickAdd waits for completion via waitForTemplaterTriggerOnCreateToComplete()
4. Done - Templater ran exactly once

When trigger_on_file_creation is disabled:

1. QuickAdd suppresses auto-trigger
2. QuickAdd creates file
3. QuickAdd calls overwriteTemplaterOnce() explicitly
4. Done - Templater ran exactly once

Changes

src/engine/TemplateEngine.ts

  • createFileWithTemplate(): Check isTemplaterTriggerOnCreateEnabled() to decide strategy
  • If enabled: don't suppress, let auto-trigger run, wait for completion
  • If disabled: suppress and call overwriteTemplaterOnce()

src/engine/CaptureChoiceEngine.ts

  • onCreateFileIfItDoesntExist(): Same mutual exclusion logic for capture-with-template flows

Test Plan

  • Create a template with Modal Forms that prompts for input
  • With Templater's trigger_on_file_creation enabled: form should appear exactly once
  • With Templater's trigger_on_file_creation disabled: form should appear exactly once
  • Capture choice with create-if-missing + template: form should appear exactly once
  • Existing overwriteFileWithTemplate behavior unchanged (modifies existing files, not affected by trigger-on-create)

Compatibility Notes

This changes behavior for users who have trigger_on_file_creation enabled:

  • Before: QuickAdd suppressed auto-trigger and ran Templater explicitly
  • After: QuickAdd lets auto-trigger run naturally

This should be transparent to users since the end result is the same (Templater processes the template), but now it happens via the auto-trigger path instead of QuickAdd's explicit call.


Open with Devin

Summary by CodeRabbit

Bug Fixes

  • Fixed a potential race condition where templates could be executed multiple times when creating new files with template or capture functionality enabled.
  • Improved coordination between template creation settings and auto-trigger functionality to ensure consistent file formatting and accurate content capture.

✏️ Tip: You can customize this high-level summary in your review settings.

When trigger_on_file_creation is enabled, stop suppressing it and then
also calling overwriteTemplaterOnce() - this created a race window where
both could fire, causing Modal Forms dialogs to appear twice.

Instead, use mutual exclusion: pick exactly ONE runner for new files.
@chhoumann chhoumann linked an issue Jan 30, 2026 that may be closed by this pull request
@vercel
Copy link

vercel bot commented Jan 30, 2026

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
quickadd Ready Ready Preview Jan 30, 2026 9:21pm

@coderabbitai
Copy link

coderabbitai bot commented Jan 30, 2026

📝 Walkthrough

Walkthrough

This PR fixes a race condition causing modal forms to execute twice when using QuickAdd with templates. Changes to CaptureChoiceEngine and TemplateEngine introduce conditional logic that coordinates templater execution, preventing duplicate invocation by either waiting for Templater's auto-trigger or explicitly invoking it once, depending on configuration.

Changes

Cohort / File(s) Summary
Templater Execution Coordination
src/engine/CaptureChoiceEngine.ts, src/engine/TemplateEngine.ts
Implements mutual-exclusion logic for templater execution by computing triggerOnCreate status and conditionally passing suppressTemplaterOnCreate to file creation. Replaces unconditional templater invocation with conditional branching: waits for auto-trigger if enabled, otherwise explicitly runs templater once. Adds follow-up read/format step in CaptureChoiceEngine after templating to ensure content consistency.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

  • #1017 — Modifies overlapping templater-related flows and utility functions (isTemplaterTriggerOnCreateEnabled, suppressTemplaterOnCreate, waitForTemplaterTriggerOnCreateToComplete).

Suggested labels

released

Poem

🐰 Two templaters dancing was quite the fright,
But a rabbit fixed the race—now just one sprite!
Conditional branches now guard the way,
Modal forms pop once, hip-hop hooray!

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately and concisely summarizes the main change: fixing a Templater double-execution race with Modal Forms.
Linked Issues check ✅ Passed The code changes implement mutual exclusion logic that directly addresses issue #1084 by preventing Modal Forms from opening twice through coordinated Templater execution.
Out of Scope Changes check ✅ Passed All changes are scoped to fixing the double-execution race: TemplateEngine.ts and CaptureChoiceEngine.ts implement mutual exclusion logic with no unrelated modifications.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch 1084-bug-modal-forms-breaking-with-quickadd-1

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@chatgpt-codex-connector chatgpt-codex-connector bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: d789670076

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

Comment on lines +522 to +526
// If trigger-on-create is enabled, let Templater's auto-trigger handle it.
// If disabled, suppress auto-trigger and run explicitly via overwriteTemplaterOnce.
// This mutual exclusion prevents the double-execution race in #1084.
const triggerOnCreate = isTemplaterTriggerOnCreateEnabled(this.app);
const suppressTemplaterOnCreate = !triggerOnCreate && filePath.toLowerCase().endsWith(".md");

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Preserve post-process-before-Templater ordering

When trigger_on_file_creation is enabled, this path intentionally skips suppression (triggerOnCreatesuppressTemplaterOnCreate = false), but the code still mutates front matter immediately after creation. Because Templater’s auto-trigger can fire before or concurrently with that post-processing, it can read unprocessed front matter (arrays coerced to strings, date tags not converted) or have its own front-matter edits overwritten by the later postProcessFrontMatter call. This breaks the existing assumption/tests that post-processing happens before Templater runs. To avoid this race, you still need a mutual exclusion mechanism (e.g., suppress until post-processing finishes, then allow the auto-trigger or explicitly call Templater once).

Useful? React with 👍 / 👎.

Copy link
Contributor

@devin-ai-integration devin-ai-integration bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

✅ Devin Review: No Issues Found

Devin Review analyzed this PR and found no potential bugs to report.

View in Devin Review to see 4 additional flags.

Open in Devin Review

@cloudflare-workers-and-pages
Copy link

Deploying quickadd with  Cloudflare Pages  Cloudflare Pages

Latest commit: d789670
Status: ✅  Deploy successful!
Preview URL: https://003e6a56.quickadd.pages.dev
Branch Preview URL: https://1084-bug-modal-forms-breakin-ezw9.quickadd.pages.dev

View logs

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[BUG] Modal Forms Breaking with QuickAdd

1 participant