CLI: console.log and return value lost when async eval awaits a vault-mutating API

Steps to reproduce

Run the following four eval commands from any shell with obsidian.exe on PATH and a vault open. Tested on Obsidian 1.12.7 (installer 1.12.4) on Windows 11.

TEST A — sync-only async + console.log: output appears as expected

obsidian eval --allow-focus-steal ‘code=(async () => {
console.log(JSON.stringify({a: 1}));
})()’

Output: {“a”:1}

TEST B — await Promise.resolve() + console.log: output appears

obsidian eval --allow-focus-steal ‘code=(async () => {
await Promise.resolve();
console.log(JSON.stringify({b: 2}));
})()’

Output: {“b”:2}

TEST D — read-only async + console.log: output appears

obsidian eval --allow-focus-steal ‘code=(async () => {
const f = app.vault.getAbstractFileByPath(“path/to/some-existing-file.md”);
const c = await app.vault.read(f);
console.log(JSON.stringify({len: c.length}));
})()’

Output: {“len”:67} (or whatever the file length is)

TEST E — vault-mutating await + console.log before AND after: NO OUTPUT

obsidian eval --allow-focus-steal ‘code=(async () => {
console.log(“before-await”);
const f = app.vault.getAbstractFileByPath(“path/to/some-existing-file.md”);
await app.fileManager.trashFile(f);
console.log(“after-trash”);
})()’

Output: (empty)

Did you follow the troubleshooting guide? [Y]

Expected result

In TEST E, both "before-await" and "after-trash" should appear on stdout, in that order, since console.log is synchronous and the calls bracket the awaited operation.

Actual result

In TEST E, stdout is empty. The => return-value line that the eval CLI normally prints is also missing. The vault mutation itself (trashFile) completes successfully — the file is moved to trash and the operation is durable — but neither the pre-await console.log nor the post-await console.log reach stdout.

The pattern is consistent: as long as the async block awaits a vault-mutating Obsidian API (app.fileManager.renameFile, app.fileManager.trashFile, app.vault.modify, etc.), ALL console.log output (including calls that execute synchronously before the await) and the eval return value disappear from stdout. Read-only awaits (app.vault.read, app.metadataCache.*) do NOT trigger the loss; only vault-mutating awaits do.

Why this is a problem

Tools that wrap obsidian eval to perform atomic file/folder operations (e.g. fileManager.renameFile for a folder rename that needs to cascade wikilinks) cannot use console.log or the return value as a success signal. Callers must work around this by re-checking state through the Local REST API plugin or another channel after the eval, which adds latency and complexity that should not be required.

Workarounds I’m using

  1. Ignore eval stdout entirely; verify the mutation landed via a separate REST GET after the eval.

  2. Wrap the rename in a higher-level command (fs:rename in my routing wrapper) that does pre-check, eval, REST verify, and only reports success if the verify lands.

These workarounds are sufficient for my own tooling, but the root cause appears to sit in the CLI plugin’s stdout-handling around Promise resolution for vault-mutating APIs. A fix at the source would let consumers trust eval output again.

Related observation: non-deterministic --allow-focus-steal

A secondary observation, possibly worth a separate report or just a documentation clarification: three identical eval invocations within the same minute produced 0× actual focus-steal on the first call and 2× on the following. The flag appears to permit focus-steal but not require it, and Obsidian’s internal scheduling decides whether the window actually grabs focus. This is fine if intentional, but documenting it explicitly would help — currently consumers may assume the flag deterministically grabs focus and gate logic on that assumption.

Environment

  • Obsidian: 1.12.7 (installer 1.12.4)

  • OS: Windows 11 Pro 22H2

  • Local REST API plugin: 3.6.x (used for verification but unrelated to the bug)

  • Reproduced consistently across multiple sessions on 2026-04-26 and 2026-04-27.

Don’t know if this will help with your issue, but update your installer to the latest one.


https://obsidian.md/help/cli#Install+Obsidian+CLI

1 Like