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:
- 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.)
- 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
- Install basic-memory 0.22.1 with
kebab_filenames: false and permalinks_include_project: true
- 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
- Call the MCP tool
write_note(title="Site Roadmap", directory="/", ...)
- 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.
Bug Description
write_notesilently creates a duplicate note when a note with the same title already exists but its file uses a different naming convention. Withkebab_filenames: false, writing a note titled "Site Roadmap" createsSite Roadmap.mdalongside an existingsite-roadmap.md, instead of updating it.The conflict detector in
EntityService.resolve_permalinkactually 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:
kebab_filenames: falsecomputesSite Roadmap.md, which does not match the existingsite-roadmap.md. (Single-word titles likeNotes.mdvsnotes.mdaccidentally survive on case-insensitive filesystems and correctly resolve toaction=Updated; only multi-word titles duplicate, because space vs hyphen differs.)permalinks_include_project: true, the new note getsmy-project/site-roadmap, which does not match the legacy permalinksite-roadmapin the existing note's frontmatter (created by an earlier version before project-prefixed permalinks).Steps To Reproduce
kebab_filenames: falseandpermalinks_include_project: truesite-roadmap.mdin a project, with frontmattertitle: Site Roadmapand a legacy non-prefixed permalink (permalink: site-roadmap), e.g. from a project created on an older versionwrite_note(title="Site Roadmap", directory="/", ...)Site Roadmap.mdis created next tosite-roadmap.md. Both notes now index separately with the same title.Expected Behavior
Either:
write_notematches the existing note (same title, same folder, filename differing only by naming convention) and updates it in place, orActual Behavior
Duplicate note created. Log from the incident (
~/.basic-memory/basic-memory.log, project name redacted):Contrast with a single-word title in the same session, which matched via the case-insensitive filesystem and updated correctly:
Resulting directory listing (duplicated pairs):
The warning originates in
src/basic_memory/services/entity_service.py(resolve_permalink), which callsdetect_file_path_conflicts, logs the conflict list, and then continues without using the result.Environment
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_noteto a multi-word title.Possible Solution
detect_file_path_conflictsreturns hits duringwrite_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.permalinks_include_projectis enabled, so notes created before project-prefixed permalinks do not fork on the next write.