CLI: concurrent `obsidian move` commands silently dropped (exit 0, no move); in large vaults the move-handler wedges into a no-op until `reload` (Windows, 1.12.7)

Steps to reproduce

  1. On Windows, ensure the latest installer (the CLI runs via the bundled Obsidian.com — the Windows console-host binary shipped next to Obsidian.exe, not a website). Open a vault.
  2. Create ~200 dummy attachments in a folder _src/.
  3. Fire obsidian move for all of them concurrently (so calls arrive faster than the app drains them):
    for f in _src/*; do obsidian move path="$f" to="_dst" & done; wait
  4. Count how many actually moved to _dst/.

Reliable (large-vault) variant that also triggers the wedge:

  1. In a larger vault (heavier per-move reindex), fire a few hundred obsidian move sequentially but fast (e.g. each call inside $(...) so its output is consumed and the shim returns before the move completes).
  2. After the burst, run a few SINGLE, well-spaced obsidian move commands.

Did you follow the troubleshooting guide?

Yes. Reproduced in the Sandbox vault with Restricted mode on (0 community plugins) on the latest version.

Expected result

Every move either succeeds (file relocated, links updated) or prints an error and returns non-zero. The handler keeps working for subsequent commands.

Actual result

  • A fraction of concurrently-fired moves return exit code 0, no stdout, no error — and the file is NOT moved (silent no-op). In the clean Sandbox this is intermittent (~1–2% under one concurrent burst); in a large vault it is severe.
  • In a large vault, after a sustained fast burst the move handler stops responding entirely: even single, well-spaced move calls become silent no-ops (empty output, exit 0, nothing moved). It only recovers after obsidian reload + ~30s.
  • Read-only commands (backlinks, unresolved, version) keep working throughout — only the mutating handler degrades.
  • The failure is load/timing-dependent: it appears when move commands arrive faster than the app processes them. Sequential firing in a small vault self-throttles (per-call latency grows with vault size) and does not drop, which is likely why earlier reports were marked resolved.
  • Secondary: CLI stdout is empty when output is redirected to a file or captured via command-substitution; it prints reliably only on an interactive TTY — so scripts get no success/failure signal (compounded by exit 0 on failure).

Environment

  • Obsidian 1.12.7 (installer 1.12.7), CLI via Obsidian.com.
  • Windows 11.
  • Reproduced in the Sandbox vault: Restricted mode on, community plugins enabled = 0.

Additional information

Likely the same IPC/named-pipe dispatch cluster as:

New vs. those threads: the wedge-until-reload behaviour (severe in large vaults), the explicit load/timing-dependence (sequential firing self-throttles and passes), and the TTY-only-stdout / empty-on-redirect behaviour.

Hypothesis (closed-source app, so speculative): the shim fires-and-exits without awaiting the async rename; commands arriving faster than the queue drains are dropped, and in a large vault a backlog wedges the handler until a reload resets it.

Suggested fix: add backpressure/acknowledgement so each move blocks until the rename resolves (or errors and returns non-zero), plus reliable non-TTY stdout.


Sandbox reproduction (verified)

Clean Sandbox vault, Restricted mode, 0 community plugins, Obsidian 1.12.7:

  • Created 200 dummy files in _src/, target folder _dst/.
  • Sequential fast-fire (200): 200/200 moved, all printed Moved: — no drops. (Small vault drains faster than the shell fires.)
  • Concurrent fire (200 backgrounded at once): 197/200 moved, 3 silently dropped — the 3 stayed in _src/ with no error emitted. A paced retry of a dropped file then succeeded (handler not wedged at this scale).
  • Repeats of the concurrent burst were inconsistent (0 drops on some runs) — the drop is timing-sensitive.
  • Bloated with several thousand extra notes + sequential fast-fire: 0 drops — the larger vault raised per-call latency, self-throttling the fire rate below the drop threshold.
  • The handler wedge (all moves no-op until reload) did not reproduce in the Sandbox; it only occurred in a larger vault (below).

Supporting evidence (larger-vault run that first surfaced this)

  • A bulk job moved a few hundred files between folders in a much larger vault by firing obsidian move in a fast loop.
  • Net result: only a minority moved; the remainder silently no-op’d (exit 0, empty output, file not moved). Afterwards even single paced move calls no-op’d until obsidian reload + ~30s.
  • Post-recovery, paced moves worked 100% (verified across repeated batches). Confirms the rate/wedge dependence.