purpose
Cloudflare Vectorize の Vector ID 上限(64 bytes )を超過して VECTOR_UPSERT_ERROR (code = 40008) で embed が失敗する問題を修正する。
premise(literal 検証済み)
Worker log の error(Cloudflare Observability、2026-04-19 17:00:43 JST)
Failed to embed doc Liplus-Project/liplus-language/docs/b.-spec-vs-implementation-order.md:
VECTOR_UPSERT_ERROR (code = 40008): id too long; max is 64 bytes, got 74 bytes
Webhook response body の整合(2026-04-19 16:29:35 JST main push)
{"docs" :{"addedOrModified" :1 ,"embedded" :0 ,"failed" :1 },
"diffs" :{"commitsProcessed" :1 ,"filesEmbedded" :0 ,"filesFailed" :1 }}
docs.failed=1 と diffs.filesFailed=1 が同じ push で発生 → Vectorize 側の共通制約違反と一致。
Byte 長の計算(literal)
Doc ID scheme : {repo}#doc-{path}
Liplus-Project/liplus-language#doc-docs/b.-spec-vs-implementation-order.md = 74 bytes (上限超過 10 bytes)
既存の短い path(例: task/Li+issues.md = 53 bytes)は収まってたので顕在化しなかった
今回 PR #1075 で作成した docs/b.- ファイル名が初の超過ケース
Diff ID scheme (PR #81 ): {repo}#commit-{sha}#file-{base64url(path)}
Liplus-Project/liplus-language#commit-0d15a0fa520ca378c1c5fe426d0c421ce3587b63#file-{base64url(docs/b.-spec-vs-implementation-order.md)} ≈ 136 bytes
base64url(path) は path × 4/3 → short path でも全体は 100 bytes 超
PR feat(diff-index): add commit diff retrieval surface (#80) #81 の diff indexing は scheme 上原理的に動いていなかった
constraints
既存の短い ID は動作しているが、長い ID では embed できない → 仕様レベルの穴
修正後の ID は 決定的 (同じ入力 → 同じ ID、idempotent)
既存 index の vector は古い ID のまま残る。search_issues は ID 形式非依存なので検索は継続機能する。完全 cleanup したいなら admin/reset-hashes 経由で再 embed
doc / issue / pr / release / diff 全 surface を同一 scheme で統一
仮修正方針(Option A: SHA256 ハッシュ)
採択 : SHA256 を base64url で 256 bits → 43 chars。type prefix 付けて常に 64 bytes 未満に収める。
新 ID 生成
// pseudocode
function stableVectorId ( prefix : string , ...parts : string [ ] ) : string {
const input = parts . join ( "\u0000" ) ; // NUL separator
const hashBytes = sha256Sync ( input ) ; // WebCrypto sync? use async
const b64url = toBase64Url ( hashBytes ) ; // 43 chars, no padding
return `${ prefix } :${ b64url } ` ; // e.g. "d:a3f4..."
}
// Per surface:
vectorId ( repo , number ) → stableVectorId ( "i" , repo , String ( number ) ) // issue/pr
docVectorId ( repo , path ) → stableVectorId ( "d" , repo , path ) // doc
releaseVectorId ( repo , tag ) → stableVectorId ( "r" , repo , tag ) // release
diffVectorId ( repo , commitSha , path ) → stableVectorId ( "c" , repo , commitSha , path ) // diff
prefix 2 bytes + : + 43 bytes = 46 bytes (64 上限から 18 bytes 余裕)。
他の選択肢
Option B: repo を numeric ID 化 — 短縮効果はあるが可読性低下、mapping 管理が必要
Option C: path 切り詰め — decisive じゃなく collision risk
→ Option A 採択。hash は deterministic なので polling の change-detection も問題なし。
target files
src/pipeline.ts — vectorId docVectorId releaseVectorId diffVectorId base64UrlEncode のロジック差し替え。新 hash ベース関数追加
src/mcp.ts — search_issues の URL builder は ID から推定してるので、metadata の file_path / doc_path / number / commit_sha から構築する形に変更(ID 逆引きは不可能になる)
src/store.ts — 変更不要(ID は Vectorize 用、store は独自 PK)
既存 vector の扱い
本 PR では既存 vector を自動 cleanup しない 。merge 後に cron poller が差分検出で新 ID を upsert する形。完全 wipe が必要なら admin /admin/reset-hashes を発動(別オペレーション)。
関連
purpose
Cloudflare Vectorize の Vector ID 上限(64 bytes)を超過して
VECTOR_UPSERT_ERROR (code = 40008)で embed が失敗する問題を修正する。premise(literal 検証済み)
Worker log の error(Cloudflare Observability、2026-04-19 17:00:43 JST)
Webhook response body の整合(2026-04-19 16:29:35 JST main push)
{"docs":{"addedOrModified":1,"embedded":0,"failed":1}, "diffs":{"commitsProcessed":1,"filesEmbedded":0,"filesFailed":1}}docs.failed=1とdiffs.filesFailed=1が同じ push で発生 → Vectorize 側の共通制約違反と一致。Byte 長の計算(literal)
Doc ID scheme:
{repo}#doc-{path}Liplus-Project/liplus-language#doc-docs/b.-spec-vs-implementation-order.md= 74 bytes(上限超過 10 bytes)task/Li+issues.md= 53 bytes)は収まってたので顕在化しなかったdocs/b.-ファイル名が初の超過ケースDiff ID scheme (PR #81):
{repo}#commit-{sha}#file-{base64url(path)}Liplus-Project/liplus-language#commit-0d15a0fa520ca378c1c5fe426d0c421ce3587b63#file-{base64url(docs/b.-spec-vs-implementation-order.md)}≈ 136 bytesconstraints
admin/reset-hashes経由で再 embed仮修正方針(Option A: SHA256 ハッシュ)
採択: SHA256 を base64url で 256 bits → 43 chars。type prefix 付けて常に 64 bytes 未満に収める。
新 ID 生成
prefix 2 bytes +
:+ 43 bytes = 46 bytes(64 上限から 18 bytes 余裕)。他の選択肢
→ Option A 採択。hash は deterministic なので polling の change-detection も問題なし。
target files
src/pipeline.ts—vectorIddocVectorIdreleaseVectorIddiffVectorIdbase64UrlEncodeのロジック差し替え。新 hash ベース関数追加src/mcp.ts— search_issues の URL builder は ID から推定してるので、metadata のfile_path/doc_path/number/commit_shaから構築する形に変更(ID 逆引きは不可能になる)src/store.ts— 変更不要(ID は Vectorize 用、store は独自 PK)既存 vector の扱い
本 PR では既存 vector を自動 cleanup しない。merge 後に cron poller が差分検出で新 ID を upsert する形。完全 wipe が必要なら admin
/admin/reset-hashesを発動(別オペレーション)。関連
#80): diff indexing 実装 — 本 bug で実効未稼働だったdocs/b.-spec-vs-implementation-order.md: 本 bug の判断記録 context