Windows: CBM_CACHE_DIR / CBM_ALLOWED_ROOT are lossily decoded — every non-ASCII char becomes U+FFFD, so the server silently uses a different path (v0.9.0)
Summary
On Windows v0.9.0, the values of CBM_CACHE_DIR and CBM_ALLOWED_ROOT are decoded
lossily: each non-ASCII character is replaced with U+FFFD (REPLACEMENT CHARACTER).
The server then operates on that mangled path instead of the one you set.
One defect, three very different-looking symptoms:
| Symptom |
Underlying cause |
config set reports success but nothing persists |
DB written to the U+FFFD twin directory |
error: cannot open config database |
mangled path is not creatable (e.g. under C:\Users\) |
every index_repository fails, even in-root |
mangled allowed-root never matches the real repo path |
CLI arguments are not affected — --repo-path with the same characters works
correctly and encodes them properly into the project name (ö → c3b6). The bug is
specific to reading these environment variables.
This is distinct from #903 / #571, which concern the repository path (there indexing
succeeds and only retrieval/naming degrades).
Environment
- codebase-memory-mcp v0.9.0,
codebase-memory-mcp-windows-amd64.zip (standard variant)
- Windows 11 Pro 10.0.26200, AMD64
- Reproduced identically from both PowerShell 5.1 and Git Bash — not a shell-quoting artifact
Proof: the tool creates a mojibake twin directory
Set the cache dir to a path with non-ASCII characters, then look at what appeared on disk:
$env:CBM_CACHE_DIR = "C:\Tëst-Ünïcode\cache"
codebase-memory-mcp config set auto_watch false # → "auto_watch = false"
codebase-memory-mcp config list # → auto_watch = false
Both commands report success. But C:\Tëst-Ünïcode\cache is never created. Instead:
Get-ChildItem C:\ -Directory | Where-Object Name -like "T*st*code" |
ForEach-Object { ($_.Name.ToCharArray() | % { "U+{0:X4}" -f [int]$_ }) -join " " }
# the directory I created:
Tëst-Ünïcode -> U+0054 U+00EB U+0073 U+0074 U+002D U+00DC U+006E U+00EF U+0063 U+006F U+0064 U+0065
# the directory the tool created:
T?st-?n?code -> U+0054 U+FFFD U+0073 U+0074 U+002D U+FFFD U+006E U+FFFD U+0063 U+006F U+0064 U+0065
Every non-ASCII codepoint has been replaced by U+FFFD. _config.db lands in the twin
directory, so settings appear to save and read back correctly while the configured
location stays empty.
Symptom 2: hard failure when the mangled path is not creatable
$env:CBM_CACHE_DIR = "C:\Users\SomeÜser\cache" # any profile with a non-ASCII name
codebase-memory-mcp config list
# → error: cannot open config database
Here the twin path would be C:\Users\SomeU+FFFDser\cache, and creating a new directory
directly under C:\Users\ is not permitted, so it fails outright instead of silently
diverging. Same physical directory reached through its ASCII 8.3 alias works fine:
$env:CBM_CACHE_DIR = "C:\Users\SOMEUS~1\cache"
codebase-memory-mcp config list # → prints config, _config.db created
This makes config get/set/list unusable on such an account — including turning
auto_watch / auto_index off.
Symptom 3: CBM_ALLOWED_ROOT rejects everything, including valid in-root paths
$env:CBM_CACHE_DIR = "C:\cbm-data" # ASCII, fine
$env:CBM_ALLOWED_ROOT = "C:\Tëst-Ünïcode"
codebase-memory-mcp cli index_repository --repo-path "C:\Tëst-Ünïcode\repro"
# → {"status":"error","outcome":"exit_nonzero",
# "hint":"Indexing worker crashed on a file. The crash was contained ..."}
The repo is inside the allowed root and must be accepted. The mangled root can never
match the real path, so the guard refuses everything. Control — same repo, variable unset:
Remove-Item Env:\CBM_ALLOWED_ROOT
codebase-memory-mcp cli index_repository --repo-path "C:\Tëst-Ünïcode\repro"
# → {"project":"C-Tc3abst-c39cnc3afcode-repro","nodes":7,"edges":7,"status":"indexed"}
Note the project name here encodes the characters correctly (ë→c3ab, Ü→c39c,
ï→c3af) — further evidence that only the env-var read path is broken.
Impact
Any Windows account whose name contains a non-ASCII character — common in German,
French, Nordic, Slavic, Turkish and CJK locales — cannot use CBM_ALLOWED_ROOT at all,
and the default cache dir under $HOME is unusable.
CBM_ALLOWED_ROOT is the documented isolation control for "agentic or multi-tenant
deployments". Losing it is bad; losing it silently is worse. Note the two failure
modes are asymmetric in danger: the hard error is loud, but the U+FFFD-twin case looks
like everything worked.
Diagnostics point away from the cause
cannot open config database reads like a permissions or file-lock problem.
Indexing worker crashed on a file. ... a future release isolates the culprit file
actively blames a source file. No file is at fault, and the worker log written to
<cache>/logs/.worker-<pid>.log is 0 bytes, so there is nothing to debug from.
Independent of the encoding fix, two cheap improvements would have saved hours here:
- Validate env-var paths at startup and fail with a message naming the variable and the
resolved path. If the resolved path differs from the input, that alone is the bug.
- Make the worker actually write its failure reason into its log file.
Workaround
Point every path at an ASCII location; a junction avoids moving anything:
New-Item -ItemType Junction -Path C:\myrepo -Target C:\Users\SomeÜser\myrepo
$env:CBM_ALLOWED_ROOT = "C:\myrepo"
$env:CBM_CACHE_DIR = "C:\cbm-data"
Also keep the binary itself on an ASCII path: when it lives under a non-ASCII directory
the supervisor cannot spawn workers and logs
index.supervisor.spawn_failed action=degrade_in_process, silently degrading to
in-process indexing.
Related: #903 (non-ASCII repo path → retrieval fails), #571 (project name strips non-ASCII).
Windows:
CBM_CACHE_DIR/CBM_ALLOWED_ROOTare lossily decoded — every non-ASCII char becomes U+FFFD, so the server silently uses a different path (v0.9.0)Summary
On Windows v0.9.0, the values of
CBM_CACHE_DIRandCBM_ALLOWED_ROOTare decodedlossily: each non-ASCII character is replaced with U+FFFD (REPLACEMENT CHARACTER).
The server then operates on that mangled path instead of the one you set.
One defect, three very different-looking symptoms:
config setreports success but nothing persistserror: cannot open config databaseC:\Users\)index_repositoryfails, even in-rootCLI arguments are not affected —
--repo-pathwith the same characters workscorrectly and encodes them properly into the project name (
ö→c3b6). The bug isspecific to reading these environment variables.
This is distinct from #903 / #571, which concern the repository path (there indexing
succeeds and only retrieval/naming degrades).
Environment
codebase-memory-mcp-windows-amd64.zip(standard variant)Proof: the tool creates a mojibake twin directory
Set the cache dir to a path with non-ASCII characters, then look at what appeared on disk:
Both commands report success. But
C:\Tëst-Ünïcode\cacheis never created. Instead:Every non-ASCII codepoint has been replaced by U+FFFD.
_config.dblands in the twindirectory, so settings appear to save and read back correctly while the configured
location stays empty.
Symptom 2: hard failure when the mangled path is not creatable
Here the twin path would be
C:\Users\SomeU+FFFDser\cache, and creating a new directorydirectly under
C:\Users\is not permitted, so it fails outright instead of silentlydiverging. Same physical directory reached through its ASCII 8.3 alias works fine:
This makes
config get/set/listunusable on such an account — including turningauto_watch/auto_indexoff.Symptom 3:
CBM_ALLOWED_ROOTrejects everything, including valid in-root pathsThe repo is inside the allowed root and must be accepted. The mangled root can never
match the real path, so the guard refuses everything. Control — same repo, variable unset:
Note the project name here encodes the characters correctly (
ë→c3ab,Ü→c39c,ï→c3af) — further evidence that only the env-var read path is broken.Impact
Any Windows account whose name contains a non-ASCII character — common in German,
French, Nordic, Slavic, Turkish and CJK locales — cannot use
CBM_ALLOWED_ROOTat all,and the default cache dir under
$HOMEis unusable.CBM_ALLOWED_ROOTis the documented isolation control for "agentic or multi-tenantdeployments". Losing it is bad; losing it silently is worse. Note the two failure
modes are asymmetric in danger: the hard error is loud, but the U+FFFD-twin case looks
like everything worked.
Diagnostics point away from the cause
cannot open config databasereads like a permissions or file-lock problem.Indexing worker crashed on a file. ... a future release isolates the culprit fileactively blames a source file. No file is at fault, and the worker log written to
<cache>/logs/.worker-<pid>.logis 0 bytes, so there is nothing to debug from.Independent of the encoding fix, two cheap improvements would have saved hours here:
resolved path. If the resolved path differs from the input, that alone is the bug.
Workaround
Point every path at an ASCII location; a junction avoids moving anything:
Also keep the binary itself on an ASCII path: when it lives under a non-ASCII directory
the supervisor cannot spawn workers and logs
index.supervisor.spawn_failed action=degrade_in_process, silently degrading toin-process indexing.
Related: #903 (non-ASCII repo path → retrieval fails), #571 (project name strips non-ASCII).