Pure-CJK (and generally non-ASCII) memories are silently never indexed → unretrievable by any search
Summary
When a memory's content contains no ASCII \w character ([A-Za-z0-9_]), the row is written to the DB but never becomes searchable — no error, no warning, no log line. Affects ~11% of our store, and 100% of pure-CJK content.
Confirmed impact on a real store (4845 memories, zh-centric)
| Population |
Count |
Self-retrievable |
content with no ASCII [A-Za-z0-9_] |
254 |
0 / 254 (0 %) |
| content with ASCII |
4591 |
4335 / 4591 |
Every single memory whose text is entirely non-ASCII is invisible to search — not "low similarity", simply absent from the candidate set.
Workaround we validated (and what does not work)
- ✅ append an ASCII anchor to the content and re-insert (
POST /v4/memories): 253/254 recovered.
- Works:
_, 7, AB, 2026_10_01, _2026-10-01, 20261001, [7]
- Does not work:
2026-10-01, [2026-10-01], (记录于 2026-10-01), 2026年10月1日
- Practical rule: the appended token needs an underscore or a ≥5-char contiguous ASCII run; isolated digits embedded in CJK don't count.
- ❌
PATCH /v4/memories with newContent does NOT re-embed — the store keeps the old vector, so patching the text changes what is displayed but not what is searchable. Repair requires insert + delete, not patch.
- ❌ Changing embedding provider/model does not help (see below).
Two smaller API issues found while debugging
POST /v4/search with limit > 100 returns 0 results (silently) instead of clamping or erroring. limit=100 → 100, limit=200 → 0. This silently broke our audit tooling.
POST /v4/search uses q as the query parameter; passing query yields a 400 with no hint about the correct name.
- Version: reproduced on
0.0.7 and 0.0.8 (self-hosted single-file binary, linux-x64)
- Deployment: Docker,
--network host, local storage, embedded local embeddings
- Embedding model:
Xenova/bge-small-zh-v1.5 (512d) — but see "not model-specific" below
Reproduce
# 1) write a pure-CJK memory
POST /v4/memories
{"containerTag":"t","memories":[{"content":"超哥是程序员。"}]}
# 2) write the same sentence with ONE ascii char appended
POST /v4/memories
{"containerTag":"t","memories":[{"content":"超哥是程序员。7"}]}
Wait for ingestion to finish, then query each with its own exact content:
POST /v4/search
{"q":"超哥是程序员。","containerTag":"t","limit":100,"threshold":0}
POST /v4/search
{"q":"超哥是程序员。7","containerTag":"t","limit":100,"threshold":0}
Expected: both are returned, ideally rank 1 with similarity ≈ 1.0.
Actual: the ASCII-containing one is returned (sim 0.99+). The pure-CJK one is never returned — not at rank 1, not anywhere in the top 100, and not for any of 28 other queries we tried, including queries whose keywords appear literally in the text (e.g. searching 台灯, 桂花树, 岳母, 廖丹 never surfaces the memory containing those exact words).
Verified on a brand-new empty store as well as on a migrated store, so it is not state corruption.
Scale in our store
Ran a self-retrieval audit (q = the memory's own exact text, limit:100, threshold:0, hit = exact content present in results):
| group |
self-retrievable |
| affected set (512 items) |
16 / 512 (3.1%) |
| random control group (200 items, ASCII-containing) |
200 / 200 (100%) |
So the control methodology is sound, and the failure is specific to non-ASCII content, not to the audit.
What we ruled out
- Tokenizer is fine. Loaded the model's own tokenizer: pure Chinese tokenizes correctly (18 tokens for an 18-char sentence), zero
[UNK], full vocab coverage.
- Query side is fine. Pure-CJK queries work — they retrieve ASCII-containing memories, and 6 mutually unrelated pure-CJK queries return largely disjoint result sets (mean pairwise Jaccard 0.029), i.e. no query-embedding collapse.
- The embedding IS computed. With
SUPERMEMORY_VERBOSE=1 the ingest log shows Embedded 1 text(s) for pure-CJK input. The vector exists — it just never becomes searchable.
- Not specific to the local ONNX path. We rebuilt against
SUPERMEMORY_EMBEDDING_PROVIDER=openai-compatible pointing at a custom deterministic embedding endpoint, bypassing ONNX entirely — the exact same failure reproduces (pure non-ASCII content unfindable, ASCII-containing content findable, in the same batch, through the same endpoint).
- Not a search-mode or threshold issue.
searchMode = memories / hybrid / documents, threshold:0, and ingesting via POST /v3/documents (status done) all fail to surface pure-CJK content.
- Not a
limit artifact. Re-audited with limit:100 (within the working range, see below) — same result.
The discriminating factor is exactly \w
Same sentence, same batch, same write call; only the appended character differs:
| appended |
a |
9 |
_ |
. |
- |
9 (fullwidth) |
七 |
α |
А (Cyrillic) |
あ |
none |
| becomes searchable |
✅ |
✅ |
✅ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
❌ |
. and - are ASCII punctuation and still fail, while _ passes — that is the fingerprint of a JavaScript \w ([A-Za-z0-9_]) test used as an index-admission guard.
Suspected cause: an index/BM25 admission (or content-cleaning) step that drops rows whose text has no \w match, so the row never enters the searchable index. We could not locate the exact line in the bundled binary, but behaviour is 10/10 consistent with \w semantics.
Suggested fix: make the guard Unicode-aware (e.g. \p{L}\p{N} with the u flag, or drop the guard), and add a test covering pure-CJK / pure-Cyrillic / pure-Greek content.
Two smaller issues found while debugging
(a) POST /v4/search with limit > 100 returns 0 results instead of clamping or erroring.
limit |
results returned |
| 50 |
50 |
| 100 |
100 |
| 200 |
0 |
| 500 |
0 |
| 1000 |
0 |
No error, HTTP 200, empty results. This silently broke our audit tooling and produced a bogus "everything is missing" report before we caught it. Should either clamp to the max or return a 400.
(b) POST /v4/search requires q, not query. Passing query returns 400 invalid_type ... path:["q"] while every other endpoint in the OpenAPI spec uses query. Also POST /v4/memories/list requires containerTags (plural) while POST /v4/memories requires containerTag (singular) — easy to trip on.
Impact note
For anyone storing non-English memory (Chinese, Japanese, Korean, Cyrillic, Greek, Arabic…), a meaningful share of entries become silently unsearchable with no signal at write time. In our case that was 512 memories, including core personal facts, discovered only by an audit months later.
Pure-CJK (and generally non-ASCII) memories are silently never indexed → unretrievable by any search
Summary
When a memory's content contains no ASCII
\wcharacter ([A-Za-z0-9_]), the row is written to the DB but never becomes searchable — no error, no warning, no log line. Affects ~11% of our store, and 100% of pure-CJK content.Confirmed impact on a real store (4845 memories, zh-centric)
[A-Za-z0-9_]Every single memory whose text is entirely non-ASCII is invisible to search — not "low similarity", simply absent from the candidate set.
Workaround we validated (and what does not work)
POST /v4/memories): 253/254 recovered._,7,AB,2026_10_01,_2026-10-01,20261001,[7]2026-10-01,[2026-10-01],(记录于 2026-10-01),2026年10月1日PATCH /v4/memorieswithnewContentdoes NOT re-embed — the store keeps the old vector, so patching the text changes what is displayed but not what is searchable. Repair requires insert + delete, not patch.Two smaller API issues found while debugging
POST /v4/searchwithlimit > 100returns 0 results (silently) instead of clamping or erroring.limit=100→ 100,limit=200→ 0. This silently broke our audit tooling.POST /v4/searchusesqas the query parameter; passingqueryyields a 400 with no hint about the correct name.0.0.7and0.0.8(self-hosted single-file binary, linux-x64)--network host, local storage, embedded local embeddingsXenova/bge-small-zh-v1.5(512d) — but see "not model-specific" belowReproduce
Wait for ingestion to finish, then query each with its own exact content:
POST /v4/search {"q":"超哥是程序员。","containerTag":"t","limit":100,"threshold":0} POST /v4/search {"q":"超哥是程序员。7","containerTag":"t","limit":100,"threshold":0}Expected: both are returned, ideally rank 1 with similarity ≈ 1.0.
Actual: the ASCII-containing one is returned (sim 0.99+). The pure-CJK one is never returned — not at rank 1, not anywhere in the top 100, and not for any of 28 other queries we tried, including queries whose keywords appear literally in the text (e.g. searching
台灯,桂花树,岳母,廖丹never surfaces the memory containing those exact words).Verified on a brand-new empty store as well as on a migrated store, so it is not state corruption.
Scale in our store
Ran a self-retrieval audit (
q= the memory's own exact text,limit:100,threshold:0, hit = exact content present in results):So the control methodology is sound, and the failure is specific to non-ASCII content, not to the audit.
What we ruled out
[UNK], full vocab coverage.SUPERMEMORY_VERBOSE=1the ingest log showsEmbedded 1 text(s)for pure-CJK input. The vector exists — it just never becomes searchable.SUPERMEMORY_EMBEDDING_PROVIDER=openai-compatiblepointing at a custom deterministic embedding endpoint, bypassing ONNX entirely — the exact same failure reproduces (pure non-ASCII content unfindable, ASCII-containing content findable, in the same batch, through the same endpoint).searchMode=memories/hybrid/documents,threshold:0, and ingesting viaPOST /v3/documents(statusdone) all fail to surface pure-CJK content.limitartifact. Re-audited withlimit:100(within the working range, see below) — same result.The discriminating factor is exactly
\wSame sentence, same batch, same write call; only the appended character differs:
a9_.-9(fullwidth)七αА(Cyrillic)あ.and-are ASCII punctuation and still fail, while_passes — that is the fingerprint of a JavaScript\w([A-Za-z0-9_]) test used as an index-admission guard.Suspected cause: an index/BM25 admission (or content-cleaning) step that drops rows whose text has no
\wmatch, so the row never enters the searchable index. We could not locate the exact line in the bundled binary, but behaviour is 10/10 consistent with\wsemantics.Suggested fix: make the guard Unicode-aware (e.g.
\p{L}\p{N}with theuflag, or drop the guard), and add a test covering pure-CJK / pure-Cyrillic / pure-Greek content.Two smaller issues found while debugging
(a)
POST /v4/searchwithlimit > 100returns 0 results instead of clamping or erroring.limitNo error, HTTP 200, empty
results. This silently broke our audit tooling and produced a bogus "everything is missing" report before we caught it. Should either clamp to the max or return a 400.(b)
POST /v4/searchrequiresq, notquery. Passingqueryreturns400 invalid_type ... path:["q"]while every other endpoint in the OpenAPI spec usesquery. AlsoPOST /v4/memories/listrequirescontainerTags(plural) whilePOST /v4/memoriesrequirescontainerTag(singular) — easy to trip on.Impact note
For anyone storing non-English memory (Chinese, Japanese, Korean, Cyrillic, Greek, Arabic…), a meaningful share of entries become silently unsearchable with no signal at write time. In our case that was 512 memories, including core personal facts, discovered only by an audit months later.