Skip to content

[Bug] memos-local-plugin: embedding source text exceeds embedding-3's 3072-token limit → HTTP 400 (code:1210), entire batch fails #2121

Description

@hitken

Pre-submission checklist | 提交前检查

  • I have searched existing issues and this hasn't been mentioned before
  • I have read the project documentation and confirmed this issue doesn't already exist
  • This issue is specific to MemOS and not a general software issue

Bug Description | 问题描述

memos-local-plugin v2.0.10 feeding untruncated/un-chunked trace text to the embedding provider causes HTTP 400 errors. When using 智谱 embedding-3 (single-request cap 3072 tokens), any traces.summary or agent_text longer than ~6KB triggers code:1210 API 调用参数有误, and because the batch is aborted on the first failure (failed = batch.length), the entire batch — including short, valid entries — is marked failed. The "修复向量 / rebuild vectors" button in the Memory Viewer returns 400 immediately, and the periodic embedding heartbeat probe shows ~60% error rate.

Environment | 环境信息

  • Plugin: @memtensor/memos-local-plugin v2.0.10 (also confirmed in v2.0.6)
  • Embedding provider: openai_compatiblehttps://open.bigmodel.cn/api/paas/v4/embeddings, model embedding-3
  • 智谱 embedding-3 official limits: 8K context window, 3072 tokens per single input, ≤64 inputs per batch (docs)
  • Storage: SQLite (memos.db), 19,114 traces
  • Host: macOS arm64 (OpenClaw runtime)

Evidence

1. DB-side: source-text length distribution

-- field                       count   max bytes
-- agent_text > 6k chars:      19      59644
-- summary     > 6k chars:     40      59644
-- user_text   > 6k chars:     21      22430

So ~40 traces exceed the 6KB (~3000 Chinese-char) mark. The longest summary and agent_text are 59,644 bytes each — roughly 30k Chinese characters, an order of magnitude over the 3072-token cap.

2. API-side: reproducing the 400

# short input → 200 OK
curl -X POST https://open.bigmodel.cn/api/paas/v4/embeddings \
  -H "Authorization: Bearer $KEY" -H "Content-Type: application/json" \
  -d '{"model":"embedding-3","input":"hello world"}'        # HTTP 200

# 50,000 'a' → 400
LONG=$(python3 -c "print('a'*50000)")
curl ... -d "{\"model\":\"embedding-3\",\"input\":\"$LONG\"}"
# → {"error":{"code":"1210","message":"API 调用参数有误,请检查文档。"}}
# HTTP 400

3. Code path: no truncation / no chunking anywhere

Three places feed source text straight into embedMany with no length guard:

core/pipeline/memory-core.ts:3998 (inside rebuildEmbeddings — the "rebuild vectors" button):

const vecs = await handle.embedder.embedMany(
  batch.map((slot) => ({ text: slot.sourceText || "(empty)", role: "document" as const })),
);

core/pipeline/memory-core.ts:4126 (slot source for vec_summary):

sourceText: row.summary?.trim() || row.userText.trim() || "(empty)",

…and the vec_action slot is built from traceActionEmbeddingText(row), which includes the full agent_text. Neither is truncated.

core/capture/embedder.ts:93 (live capture path):

return [step.agentText.trim(), toolSig].filter((s) => s.length > 0).join("\n---\n");

grep for chunk|truncat|slice|max.*token|token.*limit in embedder.ts returns only one hit — an unrelated .slice(0, 300) on tool-call inputs. The main text path is unguarded.

4. Blast radius: a single long entry nukes the whole batch

In rebuildEmbeddings:

} catch (err) {
  failed = batch.length;       // ← whole batch marked failed
  error = err instanceof Error ? err.message : String(err);
}

A 30k-char trace anywhere in the batch → 400 → the whole batch (every other short, valid trace included) is counted as failed. This is why the Memory Viewer's heartbeat shows ~94 errors out of 160 probes.

Impact

  • "Rebuild vectors" button is unusable when the DB contains any long trace — returns 400 within milliseconds.
  • Live capture silently degrades: embedSteps catches the same exception and writes null vectors for the entire step batch (see embedder.ts lines 53-58), so vector search becomes increasingly incomplete as long traces accumulate.
  • Heartbeat probe error rate ~60%, polluting the system-model-status log.
  • embedding_retry_queue accumulates: failed jobs keep retrying with the same oversized payload and never succeed.

Suggested Fixes

In rough order of effort:

  1. Truncate source text before embedding (smallest change). Add a per-input character cap (e.g. 6000 chars ≈ 2700 tokens with safety margin) in embedder.embedMany or at the collectEmbeddingSlots boundary. Log a warning when truncation fires. This won't perfectly represent long traces but keeps retrieval working.

  2. Chunked embedding for long inputs (best quality). When sourceText.length > cap, split into chunks of cap, embed each chunk separately, and average (or L2-normalize-pool) the vectors into a single embedding. The Embedder interface already returns a Float32Array, so the contract stays the same; the batch loop just needs to coalesce per-slot.

  3. Don't abort the whole batch on one failure (orthogonal but important). Either (a) send inputs one-per-request when batch mode throws, or (b) split the batch in half on exception and retry each half — so a single oversized input only fails itself, not its neighbors.

  4. Persist the provider's response body in http.non_ok warnings (also raised in LLM provider 400 errors from SiliconFlow + missing response body logging (memos-local-plugin) #1775, still relevant). Right now gateway.log shows embedding_unavailable: HTTP 400 from openai_compatible with no response body, which is why this bug took so long to pin down — the code:1210 message that points at the real cause is swallowed.

(1)+(3)+(4) together would resolve this end-to-end; (2) is the "correct" long-term fix.

How to Reproduce | 如何重现

  1. Configure plugin with embedding provider = 智谱 embedding-3.
  2. Insert (or naturally accumulate) any trace whose summary or agent_text exceeds ~6KB. The easiest way is a long multi-tool OpenClaw run — those routinely produce 30-60KB agent_text.
  3. Open the Memory Viewer → Embedding Maintenance → click "修复向量 / Rebuild".
  4. Observe: HTTP 400 returned within milliseconds, gateway.log shows [嵌入模型] · error · embedding_unavailable: HTTP 400 from openai_compatible with no body.

Additional Context

Happy to open a PR for fix (1) + (3) + (4) if maintainers are open to it — I have the code paths mapped.

Metadata

Metadata

Labels

ai:taskDispatched to AI coding agent | 已派发给 AI 编码任务ai:testingAI agent is running tests | AI 正在运行测试area:coreMOS 编排层 / 框架底座 / 跨模块问题area:pluginOpenClaw & Hermesstatus:in-progressSomeone or AI is working on it | 人工或 AI 正在处理types:bugSomething isn't working | 功能异常

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions