Skip to content

Project with empty path in config.json makes sync adopt the process cwd, mutating unrelated files #949

@groksrc

Description

@groksrc

Summary

A project entry in config.json with an empty path (observed with a cloud-mode project: "mode": "cloud", "path": "") is resolved as Path(""), which is cwd-relative. Any process that consumes that project's home (e.g. the sync/watch started by basic-memory mcp) treats whatever directory the process was launched from as the project root — and BM sync then writes frontmatter into every markdown file it finds there.

Observed behavior

While running the retrieval benchmarks (basic-memory-benchmarks), a warm basic-memory mcp stdio server was spawned with the repo as cwd, on a machine whose config.json contained:

"projects": {
  "main": { "path": "", "mode": "cloud", ... }
}

Result: the benchmark repo's own README.md, AGENTS.md, docs/benchmarks.md, and dataset READMEs all got Basic Memory frontmatter injected (title, type: note, permalink: main/...) — i.e. the main project silently adopted the repo as its local root and synced it.

Why it happens

  • ProjectConfig is built with home=Path(entry.path) (config.py:775, also :713, :1075). Path("") resolves relative to cwd.
  • The ensure_project_paths_exists validator (config.py:779) explicitly skips non-absolute paths ("cloud-only projects whose path is a slug"), so an empty path passes config load without complaint — but downstream consumers of home don't apply the same guard, so the empty path flows through to local sync as cwd.

Repro

  1. In config.json, give a project "path": "" (a cloud-mode project in a non-Postgres local install is the natural way this arises).
  2. From any directory containing markdown files, start basic-memory mcp and trigger activity.
  3. Markdown files in that directory acquire BM frontmatter under the project's permalink namespace.

Proposed fix

Two complementary guards:

  1. Validate at load: reject or warn on project entries whose path is empty/relative when they would be used as a local filesystem root (the existing validator already distinguishes absolute vs not — extend that distinction to consumers).
  2. Gate local sync by mode: a mode: "cloud" project without an initialized local sync path (bisync_initialized: false, local_sync_path: null) should never be locally synced at all, regardless of what path contains.

Happy to take this one — found it while benchmarking, and the benchmarks repo now isolates itself via BASIC_MEMORY_CONFIG_DIR (basic-memory-benchmarks#14), but the underlying footgun lives here.

🤖 Generated with Claude Code

Metadata

Metadata

Assignees

Labels

bugSomething isn't workingcloudBasic Memory Cloud
No fields configured for issues without a type.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions