{"record":{"id":"78b288c91aba3b4c","repo":"can1357/oh-my-pi","slug":"vault-write-only-supports-plain-file-paths","errorCode":null,"errorMessage":"vault:// write only supports plain file paths","messagePattern":"vault:// write only supports plain file paths","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/internal-urls/vault-protocol.ts","lineNumber":713,"sourceCode":"\t\t\tcase \"list-vaults\":\n\t\t\t\treturn this.#listVaults(parsed, context);\n\t\t\tcase \"vault-info\":\n\t\t\t\treturn this.#vaultInfo(parsed, context);\n\t\t\tcase \"fs-dir\":\n\t\t\t\treturn this.#listDir(parsed, context);\n\t\t\tcase \"fs-file\":\n\t\t\t\treturn this.#readFile(parsed, context);\n\t\t\tcase \"file-op\":\n\t\t\tcase \"vault-op\":\n\t\t\t\treturn this.#runCli(parsed, context);\n\t\t}\n\t}\n\n\tasync write(url: InternalUrl, content: string, context?: WriteContext): Promise<void> {\n\t\tif (!isVaultEnabled()) throw new VaultDisabledError();\n\t\tconst parsed = parseVaultUrl(url);\n\t\tif (parsed.kind !== \"fs-file\") {\n\t\t\tthrow new Error(\"vault:// write only supports plain file paths\");\n\t\t}\n\t\tawait this.#writeFile(parsed, content, context);\n\t}\n\n\tasync #spawn(args: string[], context?: ResolveContext | WriteContext): Promise<ObsidianSpawnResult> {\n\t\tconst bin = requireObsidianBinary(this.#resolveObsidianBinary);\n\t\treturn this.#spawnObsidian(bin, args, context?.signal, DEFAULT_OBSIDIAN_TIMEOUT_MS);\n\t}\n\n\tasync #loadVaultDirectory(context?: ResolveContext | WriteContext): Promise<Map<string, string>> {\n\t\tif (cachedVaultDirectory) return cachedVaultDirectory;\n\t\tconst result = await this.#spawn([\"vaults\", \"verbose\"], context);\n\t\tassertCliSuccess(\"vaults\", result);\n\t\tcachedVaultDirectory = parseVaultDirectory(result.stdout);\n\t\treturn cachedVaultDirectory;\n\t}\n\n\tasync #resolveVaultRoot(ref: VaultReference, context?: ResolveContext | WriteContext): Promise<string> {","sourceCodeStart":695,"sourceCodeEnd":731,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/internal-urls/vault-protocol.ts#L695-L731","documentation":"After the enabled check, VaultProtocolHandler.write() parses the URL with parseVaultUrl() and requires the result to have kind \"fs-file\" — i.e. a plain filesystem path inside a vault. Other parsed kinds (directory or CLI-backed references like attachments) are rejected because writing arbitrary content only makes sense for plain file paths. This is an input-validation error, not a state error.","triggerScenarios":"Calling handler.write(url, content) with a vault:// URL that parses to a non-fs-file kind — e.g. a directory URL like vault://_/notes/ or a URL targeting a CLI-backed resource — instead of a plain file path like vault://_/notes/idea.md.","commonSituations":"Agents or scripts generating vault:// URLs programmatically and passing directory URLs to write; mistaking the directory-listing URL form for a writable file target.","solutions":["Pass a plain file path in the vault:// URL (e.g. vault://<vault>/path/to/note.md), not a directory or CLI resource.","Check the parsed kind with parseVaultUrl() before calling write, and route directory targets to other operations (e.g. resolve for listing).","Create/choose a concrete file name if you intended to write into a directory."],"exampleFix":"// before\nawait handler.write(parseInternalUrl(\"vault://_/notes/\"), text); // directory -> throws\n// after\nawait handler.write(parseInternalUrl(\"vault://_/notes/todo.md\"), text);","handlingStrategy":"validation","validationCode":"const parsed = parseVaultUrl(url);\nif (parsed.kind !== \"fs-file\") {\n  throw new Error(`vault:// write needs a plain file path, got kind=${parsed.kind}`);\n}","typeGuard":"function isFsFileKind(parsed: ReturnType<typeof parseVaultUrl>): parsed is Extract<ReturnType<typeof parseVaultUrl>, { kind: \"fs-file\" }> {\n  return parsed.kind === \"fs-file\";\n}","tryCatchPattern":"try {\n  await handler.write(url, content);\n} catch (err) {\n  if (err instanceof Error && err.message === \"vault:// write only supports plain file paths\") {\n    // fix the URL to a plain file path and retry once\n    return;\n  }\n  throw err;\n}","preventionTips":["Always end vault:// write URLs in a concrete file name.","Validate parsed.kind before calling write; route other kinds to read/list operations.","Avoid building write URLs from directory inputs."],"tags":["vault","url-validation","input-validation"],"backgroundTag":"unsupported-url-kind","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}