Skip to content

doctor: API round-trip check fails against 202 materialization (file_write_status: pending) — and the run mutates config.json #1666

Description

@nexiouscaliver

Bug Description

bm doctor fails deterministically at its first API round-trip check on 0.23.0: the entity-create endpoint returns 202 Accepted with file_write_status: "pending" (the write is applied asynchronously by the background materialization worker, by design), but doctor checks Path.exists() immediately after the response — with no await point between, the queued write cannot have run yet, so the check can never pass.

A secondary behavior on the same invocation: bm doctor rewrites ~/.basic-memory/config.json (project create + cleanup both call save_config), stamping cloud_promo_first_run_shown / cloud_promo_last_version_shown and re-serializing the whole file. The four configured projects survive semantically, but the bytes change — anything pinning the config's hash (drift detection, benchmark runs comparing config-sha across a run) breaks after a doctor invocation.

Steps To Reproduce

  1. Install version 0.23.0 via uv tool install basic-memory==0.23.0 (also reproduced from a fresh checkout of the 0.23.0 tag)
  2. Configure ≥1 local project in ~/.basic-memory/config.json (sqlite backend)
  3. Run command bm doctor
  4. See error

Expected Behavior

Doctor completes its checks: the API-written note file is verified on disk, the manual note is indexed and searchable, the disposable project is cleaned up, and config.json is left byte-identical to before the run (the doctor project was created and deleted — the net config effect should be zero apart from any intentionally persisted state).

Actual Behavior

OK Created doctor project: doctor-2bb90ddb
Doctor failed: API note file missing: doctor/doctor-api-note.md

Reproduced 4/4 runs. The failure envelope itself carries the cause: the 202 response includes "file_write_status": "pending". A live probe of the same client stack measured the file appearing on disk 11 ms after the response (exists_immediately=False, appeared_after=0.011s). After the failed run, config.json has new bytes (promo flags stamped, full-model rewrite).

Environment

  • OS: macOS 15 (arm64)
  • Python version: 3.14 (uv-managed)
  • Basic Memory version: 0.23.0
  • Installation method: uv tool install basic-memory==0.23.0
  • Claude Desktop version: n/a (CLI + local API only)

(Follow-up note in the comments: the materialization half appears fixed on main via _read_materialized_api_note + drain_pending_materializations; this report is against 0.23.0, where the failure is deterministic.)

Activity

  1. nexiouscaliver commented on Oct 7, 2026

    @nexiouscaliver
    Author

    @phernandez — would you mind reviewing our analysis here? Two questions after digging through main:

    1. Materialization half — fixed on main, any chance of a 0.23.x backport? We traced the fix to _read_materialized_api_note + note_content_materialization.drain_pending_materializations in src/basic_memory/cli/commands/doctor.py (pinned by test_doctor_waits_for_deferred_api_note_materialization). On the 0.23.0 release, however, the failure is deterministic (the immediate Path.exists() at the old check), which makes bm doctor unusable for anyone on that version — the version a fresh uv tool install basic-memory resolved for us this week. If a 0.23.3 patch release is planned, this would be a good candidate.

    2. Config rewrite half — intended? Even with the materialization fix, a doctor invocation rewrites config.json (_delete_doctor_project_locally → save_config, plus the promo-flag stamps), so the net config effect of create-doctor-project + delete-doctor-project is non-zero bytes. We hit this concretely: our drift-detection tooling pins the config's sha256 across runs and a doctor invocation trips it (details in the issue body). If the byte churn is considered fine, no action needed — a yes/no here would let us close this half on our side.

    Happy to open a PR for either half if useful (fork + DCO + CLA flow per CONTRIBUTING — just say which).

  2. added this to the v0.24.0 milestone on Oct 9, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions