Skip to content

[BUG] write_note creates duplicate notes when existing file uses a different filename convention (kebab-case vs Title Case) #1077

Description

@stephen-ullom

Bug Description

write_note silently creates a duplicate note when a note with the same title already exists but its file uses a different naming convention. With kebab_filenames: false, writing a note titled "Site Roadmap" creates Site Roadmap.md alongside an existing site-roadmap.md, instead of updating it.

The conflict detector in EntityService.resolve_permalink actually catches this case, but it only logs a warning and proceeds to create the duplicate anyway. The MCP client never sees the warning, so the LLM believes it updated the note.

Two lookups both miss, which is why the existing note is not matched:

  1. File path: kebab_filenames: false computes Site Roadmap.md, which does not match the existing site-roadmap.md. (Single-word titles like Notes.md vs notes.md accidentally survive on case-insensitive filesystems and correctly resolve to action=Updated; only multi-word titles duplicate, because space vs hyphen differs.)
  2. Permalink: with permalinks_include_project: true, the new note gets my-project/site-roadmap, which does not match the legacy permalink site-roadmap in the existing note's frontmatter (created by an earlier version before project-prefixed permalinks).

Steps To Reproduce

  1. Install basic-memory 0.22.1 with kebab_filenames: false and permalinks_include_project: true
  2. Have an existing note site-roadmap.md in a project, with frontmatter title: Site Roadmap and a legacy non-prefixed permalink (permalink: site-roadmap), e.g. from a project created on an older version
  3. Call the MCP tool write_note(title="Site Roadmap", directory="/", ...)
  4. A new file Site Roadmap.md is created next to site-roadmap.md. Both notes now index separately with the same title.

Expected Behavior

Either:

  • write_note matches the existing note (same title, same folder, filename differing only by naming convention) and updates it in place, or
  • the detected conflict is surfaced to the caller (MCP tool response) so the client can resolve it, instead of being a log-only warning.

Actual Behavior

Duplicate note created. Log from the incident (~/.basic-memory/basic-memory.log, project name redacted):

2026-07-15 22:34:43.214 | INFO    | basic_memory.mcp.tools.write_note:write_note:227 - MCP tool call tool=write_note project=my-project directory=/, title=Site Roadmap, tags=roadmap,website
2026-07-15 22:34:43.221 | WARNING | basic_memory.services.entity_service:resolve_permalink:176 - Detected potential file path conflicts for 'Site Roadmap.md': ['site-roadmap.md']
2026-07-15 22:34:43.225 | INFO    | basic_memory.services.file_service:write_file:202 - Writing file: path=Site Roadmap.md, content_length=2582, is_markdown=True
2026-07-15 22:34:43.253 | INFO    | basic_memory.api.v2.routers.knowledge_router:create_entity:520 - API v2 response: endpoint='create_entity' external_id=... title=Site Roadmap, permalink=my-project/site-roadmap, status_code=201

Contrast with a single-word title in the same session, which matched via the case-insensitive filesystem and updated correctly:

2026-07-15 22:34:24.454 | INFO    | basic_memory.mcp.tools.write_note:write_note:388 - MCP tool response: tool=write_note project=my-project action=Updated permalink=notes ...

Resulting directory listing (duplicated pairs):

Site Roadmap.md      permalink: my-project/site-roadmap
site-roadmap.md      permalink: site-roadmap
Second Example.md    permalink: my-project/second-example
second-example.md    permalink: second-example

The warning originates in src/basic_memory/services/entity_service.py (resolve_permalink), which calls detect_file_path_conflicts, logs the conflict list, and then continues without using the result.

Environment

  • OS: macOS 26.5.2 (case-insensitive APFS)
  • Python version: 3.13.14
  • Basic Memory version: 0.22.1
  • Installation method: uv tool
  • Claude Desktop version: current (writes made via MCP write_note)

Additional Context

Relevant config values at the time:

{
  "kebab_filenames": false,
  "permalinks_include_project": true,
  "update_permalinks_on_move": false
}

The existing kebab-case notes were created by an earlier basic-memory version, before project-prefixed permalinks, so their frontmatter still carries non-prefixed permalinks. Any project in that state will fork duplicates on the next write_note to a multi-word title.

Possible Solution

  • When detect_file_path_conflicts returns hits during write_note, resolve to the existing entity (treat as update) when the title matches, or fail the write with the conflict list in the tool response so the LLM can choose. Today the result is only logged and then discarded.
  • Consider matching legacy non-prefixed permalinks when permalinks_include_project is enabled, so notes created before project-prefixed permalinks do not fork on the next write.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions