CLI `search` subcommand fails silently (exit 127) when query contains operators like tag:, path:, file:

Steps to reproduce

On Windows, run the built-in CLI with a search query that contains any of Obsidian’s search operators (tag:, path:, file:, line:):

OBS="/c/Program Files/Obsidian/Obsidian.exe"
for q in 'MeToo' 'tag:#MeToo' 'tag:inbox' 'path:Facebook' 'file:Libya' 'line:inbox'; do
  "$OBS" search query="$q" format=json 2>/tmp/err 1>/tmp/out
  echo "query=\"$q\"  exit=$?  stdout=$(wc -c < /tmp/out)B  stderr=$(wc -c < /tmp/err)B"
done

Plain-text queries work. Any query containing an operator fails.

As a sanity check, the in-app search engine itself handles these queries correctly - invoking the search plugin directly viaeval works:

"$OBS" eval code='app.internalPlugins.getPluginById("global-search").instance.openGlobalSearch("tag:#MeToo")'

So the engine is capable; the search CLI handler is the layer that drops the query.

Did you follow the troubleshooting guide? Y

Reproduced in the Sandbox vault as well. Restricted mode on, all community plugins disabled -same behavior. Reproduced in both Git Bash (MSYS2) and PowerShell 7.

Expected result

Either:

  1. The CLI returns results matching what in-app search returns for the same query (operators supported), or
  2. The CLI prints a clear error on stderr explaining that operators are not supported, with a documented non-zero exit code.

Actual result

Every query containing an operator exits with code 127, zero bytes on stdout, and zero bytes on stderr:

query="MeToo"          exit=0    stdout=209B   stderr=0B
query="tag:#MeToo"     exit=127  stdout=0B     stderr=0B
query="tag:inbox"      exit=127  stdout=0B     stderr=0B
query="path:Facebook"  exit=127  stdout=0B     stderr=0B
query="file:Libya"     exit=127  stdout=0B     stderr=0B
query="line:inbox"     exit=127  stdout=0B     stderr=0B

Same behavior with default text format and with format=json. Silent exit 127 with no stderr makes it impossible for a caller to distinguish “operator unsupported” from “query ran fine, zero matches” from “CLI crashed”.

Environment

SYSTEM INFO:
Obsidian version: 1.12.7
Installer version: 1.12.4
Operating system: Windows 11 Pro 10.0.26200
Login status: not logged in
Language: en-GB
Insider build toggle: off
Live preview: on
Base theme: adapt to system
Community theme: none
Snippets enabled: 2
Restricted mode: off
Plugins installed: 25
Plugins enabled: 21
1: Update time on edit v2.4.0
2: TimeStamper v1.3.0
3: Copilot v3.2.1
4: Commander v0.5.4
5: Callout Manager v1.1.0
6: Advanced Tables v0.22.1
7: Style Settings v1.0.9
8: Highlightr v1.2.2
9: BRAT v1.3.2
10: Auto Keyword Linker v3.3.0
11: Settings Search v1.3.10
12: Omnisearch v1.27.3
13: Local REST API v3.4.3
14: Dataview v0.5.68
15: Automatic Table Of Contents v1.7.3
16: Media Notes v1.3.0
17: Excalidraw v2.18.2
18: Editing Toolbar v3.2.1
19: Templater v2.16.4
20: Claudian v2.0.2
21: TOC compatible with Publish v3.1.0
RECOMMENDATIONS:
Custom theme and snippets: for cosmetic issues, please first try updating your theme and disabling your snippets. If still not fixed, please try to make the issue happen in the Sandbox Vault or disable community theme and snippets.
Community plugins: for bugs, please first try updating all your plugins to latest. If still not fixed, please try to make the issue happen in the Sandbox Vault or disable community plugins.

Obsidian 1.12.7 (installer 1.12.4), Windows 11 Pro 10.0.26200.

Additional information

External tooling that wraps Obsidian.exe search for tag-based or path-scoped queries silently returns “no matches” without any signal of failure. In our case this caused a multi-hour debugging session pointing at the wrong suspect (MSYS2 argv encoding, emoji handling) before we isolated the actual trigger: “query contains a : operator”. A stderr message alone would have saved
hours.

Suggested fix priority:

  1. Emit a clear stderr message and a documented exit code when the CLI cannot handle a query.
  2. Document in Obsidian.exe help search which operators are supported.
  3. Ideally, route CLI search through the same query parser as in-app search so operators work uniformly.

the correct executable for obsidian cli on windows is obsidian or obsidian.com, not obsidian.exe

Re: .com vs .exe — ran parity test on same query strings, same session:

Query Obsidian.com Obsidian.exe
search query=‘path:“Facebook Posts”’ exit 0, 7 results exit 0, 7 results
search query=“tag:#X exit 0, 18 results exit 0, 18 results

Output is bit-identical. .com is a console shim over the same binary. It changes stdio attachment, not argument parsing. Exit 127 originates from the Obsidian process itself, not from the shell failing to locate a binary (Git Bash would say command not found). So the binary is being found and executed regardless; the failure is inside the search subcommand’s operator handling.

Important correction to my original report: the failure is intermittent, not deterministic. My original post showed 5/5 exit-127 on operator queries, and that was real at the time, reproduced across Git Bash and PowerShell. But a scheduled probe I’ve been running every 10 minutes for hours over the past 48 hours has logged 43/43 exit-0 runs of the same operator query. In some Obsidian sessions, operators work; in others, they fail silently with exit 127. Same binary, same query string, same vault.

What I don’t yet know: what state inside Obsidian flips the switch. Cold-start reproduction will miss it. Long-running sessions seem more likely to trigger it, but I don’t have a confirmed trigger yet. I’ll post the probe’s state snapshot when it catches the transition.

The core claim stands: when the bug fires, CLI search returns exit 127 with zero stdout and zero stderr for any operator query, while the same query routed through openGlobalSearch via eval succeeds. The silent-exit signature is the real problem regardless of how often it fires, a caller cannot distinguish “bug” from “no matches”.

Update after a full day of follow-up testing — two findings.

1. Exit 127 still not reliably reproducible.

Ran 12 controlled scenarios against the same vault + Obsidian instance where the original 5/5 exit-127 failure was captured. Zero exit 127 in any of them. Scenarios included: cold Obsidian restart, warm process, community plugins on/off (Restricted Mode), long idle (12+ min), active UI session, Obsidian’s built-in “Rebuild vault cache”, and live incremental re-index via touchon
file mtimes. Original 5/5 is still the only capture. Consistent with “intermittent, session-dependent” as previously noted.

2. Discovered a separate, deterministic silent-failure mode.

After an Obsidian cold start, Obsidian.exe search returns exit 0 with empty JSON array stdout (2 bytes) for ALL queries, including queries that trivially have matches in the vault, like search query="path:Facebook" in a vault with a Facebook Posts/ folder containing many files. Plain text queries, tag queries, path queries. All return []. This persists indefinitely
until something initializes internal search-plugin state.

The warmup call I found:

  "$OBS" eval code="app.internalPlugins.getPluginById('global-search').instance.openGlobalSearch('anything')"

After that single eval call, subsequent Obsidian.exe search query=... calls return real results (no search view needs to persist in the UI).

Caller-side impact: exit 0 + empty-stdout for path:X is indistinguishable from “query ran fine, no matches”. Any tooling wrapping Obsidian.exe search without first invoking openGlobalSearch via eval silently returns zero results after every Obsidian restart, no signal anything is wrong. This is likely why my scheduled probe had logged 43/43 “exit 0” between incidents. Most of those were silent cold-state, not real passes. Probe has been updated to distinguish cold vs warm responses.

Suggested fix: the search subcommand should ensure internal search-plugin state is initialized before returning, transparently, without requiring callers to first invoke eval.

3. Strong negative evidence against “bug fires during re-indexing” hypothesis.

I instrumented a probe to capture app.metadataCache.inProgressTaskCount alongside CLI search results, to test whether active indexing correlates with exit-127.

Captured two full re-index cycles with concurrent CLI probing:

Test A — Settings → Files and links → Rebuild vault cache (full metadata wipe + rebuild):

  t      inProgressTaskCount    metadataCacheSize
  +33s                   558                    4      <- rebuild started, cache wiped
  +37s                   512                   49
  +40s                   468                   93
  +47s                   403                  155
  +53s                   363                  195
  +57s                   340                  218      <- CLI starts returning data (116B)
  +60s                   231                  327
  +64s                    87                  470
  +67s                     0                  556      <- complete

~36 operator-CLI calls fired during the active rebuild window. Zero exit 127.

Test B — live incremental re-index (touched 30 file mtimes in a running Obsidian, file watcher triggered rescan):

  t      inProgressTaskCount    metadataCacheSize
  +0s                     23                  558      <- index started
  +4s                     22                  562
  +8s                      0                  584      <- complete

9 operator-CLI calls fired during inProgressTaskCount > 0. Zero exit 127.

Both tests: CLI returned empty (cold-state) during active indexing but never hit the exit-127 signature. Re-indexing, whether full wipe or incremental, is not the trigger.

State-snapshot for any future captures

If anyone else wants to capture the exit-127 state alongside indexing context, the snapshot I use is:

  Obsidian.exe eval code="const m=app.metadataCache; JSON.stringify({pid: process.pid, uptime: Math.round(process.uptime()),

initialized: m.initialized, tagCount: Object.keys(m.getTags?.() || {}).length, inProgressTaskCount: m.inProgressTaskCount, mdFiles: app.vault.getMarkdownFiles().length, metadataCacheSize: Object.keys(m.metadataCache || {}).length})"

Will follow up if my probe catches the exit-127 signature again with enriched state.

I am not sure if this is your style of writing or if you are being assisted by an LLM. It’s very confusing.

I will look into what you are reporting.

From this point on, you must:

  1. Download and reinstall Obsidian.
  2. Enable restricted mode and restart Obsidian.
  3. Perform the test from Window console or Window powershell (no MSYS, or WSL, we don’t support them)
  4. Perform the test directly and manually (without agent intermidiary)
  5. Call Obsidian CLI from the terminal with obsidian or obsidian.com (no obsidian.exe)