{"record":{"id":"888c2182dc03a235","repo":"can1357/oh-my-pi","slug":"vault-oplabel-failed-detail","errorCode":null,"errorMessage":"vault://${opLabel} failed: ${detail}","messagePattern":"vault://(.+?) failed: (.+?)","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/vault-protocol.ts","lineNumber":366,"sourceCode":"\tif (bin) return bin;\n\tthrow missingBinaryError();\n}\n\nfunction cliReportedError(result: ObsidianSpawnResult): string | undefined {\n\tconst stderr = result.stderr.trim();\n\tif (stderr.startsWith(\"Error:\")) return stderr;\n\tconst stdout = result.stdout.trim();\n\tif (stdout.startsWith(\"Error:\")) return stdout;\n\treturn undefined;\n}\n\nfunction assertCliSuccess(opLabel: string, result: ObsidianSpawnResult): void {\n\tconst reportedError = cliReportedError(result);\n\tif (result.exitCode === 0 && !reportedError) return;\n\tconst stderr = result.stderr.trim();\n\tconst stdout = result.stdout.trim();\n\tconst detail = reportedError || stderr || stdout || `obsidian exited with code ${result.exitCode}`;\n\tthrow new Error(`vault://${opLabel} failed: ${detail}`);\n}\n\nfunction parseVaultDirectory(stdout: string): Map<string, string> {\n\tconst vaults = new Map<string, string>();\n\tfor (const line of stdout.split(/\\r?\\n/)) {\n\t\tconst trimmed = line.trimEnd();\n\t\tif (!trimmed) continue;\n\t\tconst tab = trimmed.indexOf(\"\\t\");\n\t\tif (tab <= 0) continue;\n\t\tconst name = trimmed.slice(0, tab);\n\t\tconst vaultPath = trimmed.slice(tab + 1).trim();\n\t\tif (!name || !vaultPath) continue;\n\t\tvaults.set(name, path.resolve(vaultPath));\n\t}\n\treturn vaults;\n}\n\nfunction parseActiveVaultPath(stdout: string): string {","sourceCodeStart":348,"sourceCodeEnd":384,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/vault-protocol.ts#L348-L384","documentation":"`assertCliSuccess` wraps every Obsidian CLI subprocess invocation. If the spawned `obsidian` process exits non-zero, or exits 0 but printed a line starting with `Error:`, the CLI's own diagnostic (stderr, stdout, or the exit code) is surfaced as `vault://<op> failed: <detail>`. The error is a faithful pass-through of the Obsidian CLI failure, not a parser bug in this library.","triggerScenarios":"#runCli / #loadVaultDirectory / #resolveVaultRoot / #vaultInfo spawn the Obsidian CLI and it fails: vault not found for the requested ref, daily-notes plugin not configured, Obsidian CLI too old and not accepting the subcommand, vault locked/unavailable, or the CLI prints `Error: ...` on stdout/stderr.","commonSituations":"Referencing a vault name that no longer exists or was renamed; Obsidian not running / CLI plugin version mismatch; daily notes core plugin disabled so `daily`/`daily-path` fail; transient Obsidian crashes or timeout kills (30s default) reported as a non-zero exit.","solutions":["Read the `detail` suffix — it is the Obsidian CLI's own error; fix the underlying cause it names.","Run `vault://_/` (list vaults) and confirm the vault name in your URL matches an existing vault.","Update the Obsidian app/CLI to a version supporting the subcommand (e.g. `base:query`, `search:context`).","Enable the required core plugins (Daily notes for `daily`, Bases for `base`) and retry.","If the CLI is transiently failing, retry the operation after confirming Obsidian is running."],"exampleFix":"// before\nconst res = await handler.resolve(parseInternalUrl(\"vault://OldName/?op=daily\"));\n// vault://daily failed: vault \"OldName\" not found\n// after: discover the actual vault name first\nconst vaults = await handler.resolve(parseInternalUrl(\"vault://_/\"));\nconst res = await handler.resolve(parseInternalUrl(`vault://${encodeURIComponent(correctName)}/?op=daily`));","handlingStrategy":"retry","validationCode":"// Before invoking, verify preconditions the CLI depends on:\nconst vaults = await handler.resolve(parseInternalUrl(\"vault://_/\")); // throws a clear error if CLI/vaults unavailable\n// confirm the vault name exists in vaults.textContent before targeting vault://<Name>/...","typeGuard":"function isCliFailure(err: unknown): err is Error & { message: string } {\n  return err instanceof Error && /^vault:\\/\\/.+ failed: /.test(err.message);\n}","tryCatchPattern":"try {\n  const res = await handler.resolve(parseInternalUrl(url));\n} catch (err) {\n  if (isCliFailure(err)) {\n    const detail = err.message.replace(/^vault:\\/\\/.+ failed: /, \"\");\n    logger.warn(\"obsidian cli failed\", { url, detail });\n    // retry once for transient failures; otherwise surface `detail` to the user\n  } else throw err;\n}","preventionTips":["Keep the Obsidian app and its CLI current — old CLIs reject newer subcommands","Verify vault names with vault://_/ before referencing them","Enable required core plugins (Daily notes, Bases) for daily/base ops","Treat the message suffix as the authoritative diagnostic — it is the CLI's own stderr/stdout"],"tags":["subprocess","cli","external-process","obsidian"],"backgroundTag":"cli-command-failed","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}