Skip to content

bug(pipeline): Vectorize vector ID exceeds 64 byte limit for long paths and commit diffs #83

Description

@liplus-lin-lay

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=1diffs.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.tsvectorId 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 を発動(別オペレーション)。

関連

Metadata

Metadata

Labels

bug動いていない、壊れているready本文が実装開始できる形まで収束している状態。ただし更新は継続可能

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions