Why does getFileByPath return null when the file exists?

I am writing my first plugin in which I manipulate a file called daylies.md (set via settings). Here comes the code and my problems are below the code. Right now the interesting part is when I retrieve the file handler (Tfile | null) and check whether the file exists - if not I create a new one. In my case right now the file exists.

import {
	App,
	Editor,
	MarkdownView,
	Modal,
	Notice,
	Plugin,
	PluginSettingTab,
	Setting,
	Vault,
	moment,
} from "obsidian";

interface DayliesPluginSettings {
	dayliesFilename: string;
}

const DEFAULT_SETTINGS: DayliesPluginSettings = {
	dayliesFilename: "/daylies.md",
};

export default class DayliesPlugin extends Plugin {
	settings: DayliesPluginSettings;

	async onload() {
		await this.loadSettings();

		let dayliesFileHandler = this.app.vault.getFileByPath(
			this.settings.dayliesFilename
		);
		console.log(`got dayliesFileHandler: ${dayliesFileHandler}`);
		if (dayliesFileHandler === null) {
			console.warn("creating new daylies file");
			dayliesFileHandler = await this.app.vault.create(
				this.settings.dayliesFilename,
				""
			);
		}
		const content: string = await this.app.vault.read(dayliesFileHandler);
		console.log(`content of daylies: ${JSON.stringify(content)}`);
		const newContent = updateDaylies(content);
		if (newContent !== null) {
			await this.app.vault.modify(dayliesFileHandler, newContent);
			console.log("updated daylies with current date");
		}
	}

	onunload() {}

	async loadSettings() {
		this.settings = Object.assign(
			{},
			DEFAULT_SETTINGS,
			await this.loadData()
		);
	}

	async saveSettings() {
		await this.saveData(this.settings);
	}
}

// updateDaylies returns a new content with the appended part for today, or null if it had already been done
const updateDaylies = (content: string): string | null => {
	const todayISO = moment().format("YYYY-MM-DD");
	// check if the header for today is there
	const regex = new RegExp(`# ${todayISO}`);
	if (regex.test(content)) {
		return null;
	} else {
		// we do not have today's date in the file
		return `# ${todayISO}\n\n`;
	}
};

I systematically get the following error in the console:

got dayliesFileHandler: null
plugin:daylies:42 creating new daylies file
onload @ plugin:daylies:42
await in onload (async)
(anonymous) @ app.js:1
(anonymous) @ app.js:1
(anonymous) @ app.js:1
(anonymous) @ app.js:1
g @ app.js:1
t.load @ app.js:1
(anonymous) @ app.js:1
(anonymous) @ app.js:1
(anonymous) @ app.js:1
a @ app.js:1
Promise.then (async)
l @ app.js:1
(anonymous) @ app.js:1
g @ app.js:1
e.loadPlugin @ app.js:1
(anonymous) @ app.js:1
(anonymous) @ app.js:1
(anonymous) @ app.js:1
(anonymous) @ app.js:1
g @ app.js:1
e.enablePlugin @ app.js:1
reload @ plugin:hot-reload:110
await in reload (async)
eval @ plugin:hot-reload:92
Promise.then (async)
run @ plugin:hot-reload:10
eval @ plugin:hot-reload:92
l @ app.js:1
c @ app.js:1
setTimeout (async)
u @ app.js:1
onFileChange @ plugin:hot-reload:94
checkVersions @ plugin:hot-reload:59
await in checkVersions (async)
eval @ plugin:hot-reload:14
Promise.then (async)
run @ plugin:hot-reload:10
eval @ plugin:hot-reload:14
l @ app.js:1
c @ app.js:1
setTimeout (async)
c @ app.js:1
setTimeout (async)
u @ app.js:1
e.tryTrigger @ app.js:1
e.trigger @ app.js:1
t.trigger @ app.js:1
t.onChange @ app.js:1
e.trigger @ app.js:1
(anonymous) @ app.js:1
(anonymous) @ app.js:1
(anonymous) @ app.js:1
(anonymous) @ app.js:1
g @ app.js:1
e.reconcileFile @ app.js:1
(anonymous) @ app.js:1
n @ app.js:1
Promise.then (async)
e.queue @ app.js:1
(anonymous) @ app.js:1
setTimeout (async)
e.onFileChange @ app.js:1
(anonymous) @ app.js:1
emit @ node:events:519
FSWatcher._handle.onchange @ node:internal/original-fs/watchers:215
app.js:1 Plugin failure: daylies 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)
(anonymous) @ app.js:1
(anonymous) @ app.js:1
(anonymous) @ app.js:1
s @ app.js:1
Promise.then (async)
l @ app.js:1
(anonymous) @ app.js:1
g @ app.js:1
e.enablePlugin @ app.js:1
reload @ plugin:hot-reload:110
await in reload (async)
eval @ plugin:hot-reload:92
Promise.then (async)
run @ plugin:hot-reload:10
eval @ plugin:hot-reload:92
l @ app.js:1
c @ app.js:1
setTimeout (async)
u @ app.js:1
onFileChange @ plugin:hot-reload:94
checkVersions @ plugin:hot-reload:59
await in checkVersions (async)
eval @ plugin:hot-reload:14
Promise.then (async)
run @ plugin:hot-reload:10
eval @ plugin:hot-reload:14
l @ app.js:1
c @ app.js:1
setTimeout (async)
c @ app.js:1
setTimeout (async)
u @ app.js:1
e.tryTrigger @ app.js:1
e.trigger @ app.js:1
t.trigger @ app.js:1
t.onChange @ app.js:1
e.trigger @ app.js:1
(anonymous) @ app.js:1
(anonymous) @ app.js:1
(anonymous) @ app.js:1
(anonymous) @ app.js:1
g @ app.js:1
e.reconcileFile @ app.js:1
(anonymous) @ app.js:1
n @ app.js:1
Promise.then (async)
e.queue @ app.js:1
(anonymous) @ app.js:1
setTimeout (async)
e.onFileChange @ app.js:1
(anonymous) @ app.js:1
emit @ node:events:519
FSWatcher._handle.onchange @ node:internal/original-fs/watchers:215

I am lost in what happens between the line let dayliesFileHandler = this.app.vault.getFileByPath(...) and the if.

The log message says that there is no file (null is returned). This is not true, but anyway. Then I check for null, and attempt to create the file which does exist.

My question: why is null returned by this.app.vault.getFileByPath()?

Because you have leading slash in /daylies.md. Remove it and your code will work.

Alternatively you can use this.app.vault.getFileByPath(normalizePath(this.settings.dayliesFilename))

I had just daylies.md initially and the problem is the same (I tried an absolute path just in case).

I also tried with normalizePath but there is no change either.

What is interesting is that I see this behaviour when completely reloading the plugin via F5 (this also cleans the console).

When I edit the code and the plugin is relaoded via the hot-reload plugin the error does not appear.

  1. Insert console.log(this.settings.dayliesFilename) before getFileByPath

  2. Move your code logic from onload() into

this.app.workspace.onLayoutReady(async () => {
  // here
});
1 Like

Ahhh, thank you it works now. I will dig into onLayoutReady to understand what just happened :slight_smile:

This topic was automatically closed 28 days after the last reply. New replies are no longer allowed.