Skip to content

Commit 186eebd

Browse files
committed
fix: resolve vault cache synchronization issues with file and folder operations
- Normalize all file paths to use forward slashes for consistent Obsidian vault lookups - Handle Obsidian vault cache stale state when folders/files are created but not reflected immediately - Gracefully update existing files instead of failing when vault cache is out of sync - Add retry logic with small delay for folder creation conflicts - Remove all debug console logging Fixes issue where syncing GitHub issues would fail with 'File already exists' and 'Folder already exists' errors even when folders/files didn't exist in the vault cache.
1 parent 047c8b4 commit 186eebd

4 files changed

Lines changed: 123 additions & 26 deletions

File tree

src/file-manager.ts

Lines changed: 22 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -295,7 +295,9 @@ export class FileManager {
295295
this.settings.dateFormat,
296296
);
297297
const fileName = `${baseFileName}.md`;
298-
const filePath = `${folderPath}/${fileName}`;
298+
// Normalize folder path to use forward slashes for consistent vault lookups
299+
const normalizedFolderPath = folderPath.replace(/\\/g, "/");
300+
const filePath = `${normalizedFolderPath}/${fileName}`;
299301

300302
const existingFile = this.app.vault.getAbstractFileByPath(filePath);
301303
let fileContent = await this.generateProjectItemContent(
@@ -323,7 +325,25 @@ export class FileManager {
323325
}
324326
await this.app.vault.modify(existingFile, fileContent);
325327
} else {
326-
await this.app.vault.create(filePath, fileContent);
328+
try {
329+
await this.app.vault.create(filePath, fileContent);
330+
} catch (fileCreateError: unknown) {
331+
const errorMsg = fileCreateError instanceof Error ? fileCreateError.message : String(fileCreateError);
332+
333+
// Check if file exists due to stale cache
334+
const fileCheck = this.app.vault.getAbstractFileByPath(filePath);
335+
336+
if (fileCheck instanceof TFile) {
337+
338+
// File exists but wasn't detected before - update it
339+
const existingContent = await this.app.vault.read(fileCheck);
340+
await this.app.vault.modify(fileCheck, fileContent);
341+
this.noticeManager.debug(`Updated existing project item file for #${content.number} (file existed but cache was stale)`);
342+
} else {
343+
// File creation genuinely failed - rethrow
344+
throw fileCreateError;
345+
}
346+
}
327347
}
328348
createdCount++;
329349
}

src/issue-file-manager.ts

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -110,8 +110,10 @@ export class IssueFileManager {
110110
);
111111
}
112112

113+
// Normalize folder path to use forward slashes for consistent vault lookups
114+
const normalizedIssueFolderPath = issueFolderPath.replace(/\\/g, "/");
113115
const file = this.app.vault.getAbstractFileByPath(
114-
`${issueFolderPath}/${fileName}`,
116+
`${normalizedIssueFolderPath}/${fileName}`,
115117
);
116118

117119
const [owner, repoName] = repo.repository.split("/");
@@ -288,11 +290,30 @@ export class IssueFileManager {
288290
}
289291
}
290292
} else {
291-
await this.app.vault.create(
292-
`${issueFolderPath}/${fileName}`,
293-
content,
294-
);
295-
this.noticeManager.debug(`Created issue file for ${issue.number}`);
293+
// Normalize path to use forward slashes consistently
294+
const normalizedFolderPath = issueFolderPath.replace(/\\/g, "/");
295+
const filePathToCreate = `${normalizedFolderPath}/${fileName}`;
296+
297+
try {
298+
await this.app.vault.create(filePathToCreate, content);
299+
this.noticeManager.debug(`Created issue file for ${issue.number}`);
300+
} catch (fileCreateError: unknown) {
301+
const errorMsg = fileCreateError instanceof Error ? fileCreateError.message : String(fileCreateError);
302+
303+
// Check if file exists due to stale cache
304+
const fileCheck = this.app.vault.getAbstractFileByPath(filePathToCreate);
305+
306+
if (fileCheck instanceof TFile) {
307+
// File exists but wasn't detected before - update it
308+
const existingContent = await this.app.vault.read(fileCheck);
309+
await this.app.vault.modify(fileCheck, content);
310+
this.noticeManager.debug(`Updated existing issue file for ${issue.number} (file existed but cache was stale)`);
311+
return;
312+
}
313+
314+
// File creation genuinely failed - rethrow
315+
throw fileCreateError;
316+
}
296317
}
297318
}
298319

src/pr-file-manager.ts

Lines changed: 27 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -113,8 +113,10 @@ export class PullRequestFileManager {
113113
);
114114
}
115115

116+
// Normalize folder path to use forward slashes for consistent vault lookups
117+
const normalizedPullRequestFolderPath = pullRequestFolderPath.replace(/\\/g, "/");
116118
const file = this.app.vault.getAbstractFileByPath(
117-
`${pullRequestFolderPath}/${fileName}`,
119+
`${normalizedPullRequestFolderPath}/${fileName}`,
118120
);
119121

120122
const [owner, repoName] = repo.repository.split("/");
@@ -246,11 +248,30 @@ export class PullRequestFileManager {
246248
}
247249
}
248250
} else {
249-
await this.app.vault.create(
250-
`${pullRequestFolderPath}/${fileName}`,
251-
content,
252-
);
253-
this.noticeManager.debug(`Created PR file for ${pr.number}`);
251+
// Normalize path to use forward slashes consistently
252+
const normalizedFolderPath = pullRequestFolderPath.replace(/\\/g, "/");
253+
const filePathToCreate = `${normalizedFolderPath}/${fileName}`;
254+
255+
try {
256+
await this.app.vault.create(filePathToCreate, content);
257+
this.noticeManager.debug(`Created PR file for ${pr.number}`);
258+
} catch (fileCreateError: unknown) {
259+
const errorMsg = fileCreateError instanceof Error ? fileCreateError.message : String(fileCreateError);
260+
261+
// Check if file exists due to stale cache
262+
const fileCheck = this.app.vault.getAbstractFileByPath(filePathToCreate);
263+
264+
if (fileCheck instanceof TFile) {
265+
// File exists but wasn't detected before - update it
266+
const existingContent = await this.app.vault.read(fileCheck);
267+
await this.app.vault.modify(fileCheck, content);
268+
this.noticeManager.debug(`Updated existing PR file for ${pr.number} (file existed but cache was stale)`);
269+
return;
270+
}
271+
272+
// File creation genuinely failed - rethrow
273+
throw fileCreateError;
274+
}
254275
}
255276
}
256277

src/util/file-helpers.ts

Lines changed: 47 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,4 @@
1-
import { App, TFile } from "obsidian";
1+
import { App, TFile, TFolder } from "obsidian";
22
import { format } from "date-fns";
33
import { escapeBody } from "./escapeUtils";
44
import { NoticeManager } from "../notice-manager";
@@ -50,19 +50,54 @@ export class FileHelpers {
5050
return;
5151
}
5252

53-
const folder = this.app.vault.getAbstractFileByPath(path);
54-
if (!folder) {
55-
try {
56-
await this.app.vault.createFolder(path);
57-
this.noticeManager.debug(`Created folder: ${path}`);
58-
} catch (error) {
59-
// Folder may have been created concurrently or vault cache was stale - verify it exists now
60-
const existsNow = this.app.vault.getAbstractFileByPath(path);
61-
if (!existsNow) {
62-
// Folder truly doesn't exist and creation failed - rethrow
63-
throw error;
53+
// Normalize path separators to forward slashes for consistency
54+
const normalizedPath = path.replace(/\\/g, "/");
55+
let existing = this.app.vault.getAbstractFileByPath(normalizedPath);
56+
57+
// Check if folder already exists
58+
if (existing instanceof TFolder) {
59+
return;
60+
}
61+
62+
63+
64+
try {
65+
await this.app.vault.createFolder(normalizedPath);
66+
this.noticeManager.debug(`Created folder: ${normalizedPath}`);
67+
} catch (error: unknown) {
68+
const errorMsg = error instanceof Error ? error.message : String(error);
69+
70+
// Handle "Folder already exists" or other folder creation errors
71+
// Retry vault check with slight delay to allow cache to update
72+
const existsNow = this.app.vault.getAbstractFileByPath(normalizedPath);
73+
74+
if (existsNow instanceof TFolder) {
75+
// Folder exists now, which is fine (concurrent creation or cache stale)
76+
return;
77+
}
78+
79+
if (
80+
error instanceof Error &&
81+
error.message.includes("Folder already exists")
82+
) {
83+
// Expected case - folder was created successfully but Obsidian threw error anyway
84+
// This commonly happens when the vault cache is out of sync
85+
// Try one more time with a tiny delay to let cache update
86+
await new Promise(resolve => setTimeout(resolve, 10));
87+
const retryCheck = this.app.vault.getAbstractFileByPath(normalizedPath);
88+
if (retryCheck instanceof TFolder) {
89+
this.noticeManager.debug(
90+
`Folder created successfully: ${normalizedPath}`,
91+
);
92+
return;
6493
}
94+
// Even though cache says it doesn't exist, the folder was likely created
95+
// Continue anyway - subsequent operations will work since the folder actually exists
96+
return;
6597
}
98+
99+
// Folder creation genuinely failed - rethrow
100+
throw error;
66101
}
67102
}
68103

0 commit comments

Comments
 (0)