Skip to content

Fix production capability vector reindex resilience - #625

Merged
kody-bot merged 1 commit into
mainfrom
cursor/capability-vector-reindex-2b1f
Jul 5, 2026
Merged

kody-bot merged 1 commit into
mainfrom
cursor/capability-vector-reindex-2b1f

Conversation

@kentcdodds

@kentcdodds kentcdodds commented Jul 5, 2026 •

Copy link
Copy Markdown
Owner

Summary

  • Make optional AI_GATEWAY_ID routing fail-open: if Workers AI via AI Gateway throws, embeddings retry direct Workers AI and log a structured warning.
  • Bound embedding calls with safe batching and input truncation, then make reindex upserts recursively split failed batches down to individual vectors so one bad item is skipped/reported instead of aborting an entire phase.
  • Return structured maintenance failure JSON with failing phase/cause and print the response body from the deploy workflow on non-2xx reindex responses.

Root-cause analysis

Most plausible root cause is AI Gateway misconfiguration exposed by #613. AI_GATEWAY_ID had been synced as an optional secret but unused; after #613, every embedding call passed that id into env.AI.run. If the gateway id is stale/nonexistent in the Cloudflare account, the first embedding request throws and the production maintenance endpoint returns 500. The code also had two secondary production risks: it did not enforce Workers AI embedding batch/input limits in embedTextsForVectorize, and reindex batches were all-or-nothing on embed/upsert failures.

I cannot verify account-level Cloudflare Gateway configuration from the repo. Kent should check the production AI_GATEWAY_ID secret: either create/fix the matching AI Gateway in the Cloudflare account, or unset the secret. With this PR, an invalid gateway no longer blocks embeddings because direct Workers AI is retried automatically.

Validation

  • npx vitest run --project node-unit packages/worker/src/capability-maintenance.node.test.ts packages/worker/src/package-registry/package-reindex.node.test.ts packages/worker/src/mcp/capabilities/reindex-batches.node.test.ts
  • npx vitest run --project workers-unit packages/worker/src/mcp/capabilities/capability-search.workers.test.ts
  • npm run typecheck
  • npm run test:e2e:run
  • npm run validate (pass; first validate attempt hit a transient Playwright ssr-hydration socket hang-up, then E2E and full validate passed on rerun)
System recap — extends existing primitives (medium risk)

Mode: recap · Base: main @ 7fb5290 · Head: dcec519

Classification: extends — this changes vector embedding/reindex failure behavior and maintenance response shape, without adding a new primitive.

Primitives touched

Primitive Group Impact
vectorize-search storage extends — Workers AI embedding calls are batched/truncated, gateway routing is fail-open, and vector reindexing isolates failed items
capability-registry assistant composes — builtin capability vectors use the shared tolerant reindex helper
memories assistant composes — memory vectors use the shared tolerant reindex helper with existing userId metadata
jobs assistant composes — job vectors use the shared tolerant reindex helper with existing userId metadata
saved-packages assistant composes — saved package vectors distinguish manifest-load failures from embed/upsert failures
scheduled-cron surfaces extends — maintenance failures include structured phase/cause JSON

System map

flowchart LR
	maintenance["scheduled-cron / maintenance"]:::extended --> vectorizeSearch["vectorize-search"]:::extended
	capabilityRegistry["capability-registry"]:::touched --> vectorizeSearch
	memories["memories"]:::touched --> vectorizeSearch
	jobs["jobs"]:::touched --> vectorizeSearch
	savedPackages["saved-packages"]:::touched --> vectorizeSearch
	d1AppDb["d1-app-db"]:::untouched --> memories
	d1AppDb --> jobs
	d1AppDb --> savedPackages
	classDef touched fill:#1a7f37,color:#fff
	classDef extended fill:#9a6700,color:#fff
	classDef added fill:#cf222e,color:#fff
	classDef untouched fill:#57606a,color:#fff
Loading

Change flow

sequenceDiagram
	participant Deploy
	participant Maintenance
	participant Embed as Workers AI embedding
	participant Vectorize
	Deploy->>Maintenance: POST /__maintenance/reindex-capabilities
	Maintenance->>Embed: embed chunks (gateway if configured)
	Embed-->>Maintenance: gateway error
	Maintenance->>Embed: retry direct Workers AI
	Maintenance->>Vectorize: upsert vector batch
	Vectorize-->>Maintenance: item/batch failure
	Maintenance->>Maintenance: split batch to isolate item
	Maintenance-->>Deploy: JSON ok/warning or structured fatal phase
Loading

Invariants

  • Preserves per-user-isolation: memory, job, and package vector metadata continue to carry userId; builtin vectors remain global with kind: builtin.
Open in Web Open in Cursor 

Summary by CodeRabbit

  • New Features

    • Reindex operations now provide clearer progress and failure details, including partial-success warnings and per-item failure summaries.
    • Large embedding requests are processed in smaller chunks for more reliable handling.
  • Bug Fixes

    • Improved resilience when embedding requests fail, including retrying without gateway routing when needed.
    • Reindex jobs now continue past individual item failures and report what was skipped instead of failing silently.
    • Production deploy checks now validate reindex responses more strictly and surface the returned details in logs.

Co-authored-by: Kent C. Dodds <me+github@kentcdodds.com>
@coderabbitai

coderabbitai Bot commented Jul 5, 2026 •

Copy link
Copy Markdown

Review Change Stack

📝 Walkthrough

Walkthrough

This PR introduces a shared reindexVectorCandidates helper in reindex-batches.ts that batches embedding and vector upsert operations with recursive failure isolation. Job, capability, memory, and package reindex flows are refactored to delegate to this helper, returning structured VectorReindexResult with failures. Embedding logic gains truncation, batching, and gateway-fallback retry. Capability maintenance error reporting is enhanced with per-phase failure summaries, and the deploy workflow's reindex health check now inspects HTTP status and response body.

Changes

Vector Reindex Refactor

Layer / File(s) Summary
Shared reindex helper
packages/worker/src/mcp/capabilities/reindex-batches.ts, reindex-batches.node.test.ts
New reindexVectorCandidates batches embedding/upsert calls, recursively splits and records per-candidate failures (embed/upsert phases), caps reported failures, and returns aggregated upserted/failed/warning/error, validated by new tests.
Embedding batching and gateway fallback
packages/worker/src/mcp/capabilities/capability-search.ts, capability-search.workers.test.ts
Adds embedding batch size and max-input-length constants with truncation, batches Workers AI calls, and adds gateway-to-direct retry fallback with combined error messages on failure.
Reindex callers delegate to shared helper
packages/worker/src/jobs/job-reindex.ts, packages/worker/src/mcp/capabilities/capability-reindex.ts, packages/worker/src/mcp/memory/memory-reindex.ts, packages/worker/src/package-registry/package-reindex.ts, package-reindex.node.test.ts
Job, capability, memory, and package reindex functions build candidate lists and delegate to reindexVectorCandidates, returning VectorReindexResult; package reindex separately tracks manifest load failures merged with reindex failures.
Maintenance failure aggregation and deploy health check
packages/worker/src/capability-maintenance.ts, capability-maintenance.node.test.ts, .github/workflows/deploy.yml
ReindexStepResult/new ReindexFailureSummary types support throwing MaintenanceFailureError with structured failedPhases; deploy workflow's reindex curl step now checks HTTP status and logs response body, failing on non-2xx.

Estimated code review effort: 4 (Complex) | ~60 minutes

Possibly related PRs

  • kentcdodds/kody#133: Both touch the memory reindex flow in packages/worker/src/mcp/memory/memory-reindex.ts, one refactoring it to use the shared helper and the other adding the original implementation.
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title matches the main change set: improving production capability vector reindex resilience and failure handling.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch cursor/capability-vector-reindex-2b1f

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 5, 2026

Copy link
Copy Markdown
Contributor

🔎 Preview deployed: https://kody-pr-625.kentcdodds.workers.dev

Worker: kody-pr-625
D1: kody-pr-625-db
KV: kody-pr-625-oauth-kv

Mocks:

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

🧹 Nitpick comments (6)
.github/workflows/deploy.yml (2)

271-283: 🩺 Stability & Availability | 🔵 Trivial

Confirm 120s --max-time still fits the new resilience path.

The recursive per-item split/retry and AI Gateway fallback added elsewhere in this reindex stack can meaningfully lengthen total reindex duration (extra round trips per failed batch/vector plus a direct-Workers-AI retry on gateway failure). A hardcoded 120s timeout on this health-check curl could start failing production deploys with false negatives once real vector volume/failure rates trigger the new fallback paths, even though the underlying reindex work is progressing correctly.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/deploy.yml around lines 271 - 283, The reindex deploy
check in the workflow still uses a hardcoded 120s curl timeout, which may be too
short now that the reindex path can recurse per item and fall back through AI
Gateway retries. Review the `status="$(curl ... --max-time 120 ...
__maintenance/reindex-capabilities)"` step and adjust the timeout strategy to
accommodate the longer worst-case duration, or make it configurable so the
`reindex-capabilities` health check does not fail deploys prematurely.

278-279: 🔒 Security & Privacy | 🔵 Trivial | ⚡ Quick win

Consider trimming the response body before printing to CI logs.

cat reindex-response.json dumps the full JSON body (including per-phase failedPhases and any nested per-vector failures with id/error) into the workflow log. Even with capped failure lists upstream, this puts internal record identifiers for capabilities/jobs/memories/packages into CI logs. Consider printing only a summary (e.g., jq '{ok, failure}') unless full detail is needed for debugging.

♻️ Example
-          cat reindex-response.json
+          jq '{ok, error, failure}' reindex-response.json 2>/dev/null || cat reindex-response.json
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.github/workflows/deploy.yml around lines 278 - 279, The workflow step that
prints reindex results is exposing the full reindex-response JSON to CI logs.
Update the deploy workflow’s reindex logging to avoid using the raw cat
reindex-response.json output and instead print only a minimal summary from the
response, keeping detailed failure fields out of the logs unless explicitly
needed for debugging. Use the existing reindex response handling in the deploy
job to locate the log statement and replace it with a summary-focused print.
packages/worker/src/mcp/capabilities/reindex-batches.node.test.ts (1)

14-98: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Add coverage for upsert failure isolation.

The helper’s core behavior includes recursively splitting failed upsert batches, but these tests only cover embedding failures. Add a case where the initial index.upsert() rejects for one vector and assert successful vectors are upserted without extra embedding calls.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/mcp/capabilities/reindex-batches.node.test.ts` around
lines 14 - 98, Add a test in reindex-batches.node.test.ts to cover upsert
failure isolation in reindexVectorCandidates, since the current cases only
verify embedding failures. Mock index.upsert to reject for a batch containing
one bad vector so the helper recursively splits the batch, then assert the
successful vectors are still upserted and embedTextsForVectorize is not called
again during the retry path. Use the existing reindexVectorCandidates,
embedTextsForVectorize, and index.upsert symbols to locate the behavior and keep
the assertions aligned with the current failure-shaping pattern.
packages/worker/src/mcp/capabilities/capability-search.ts (2)

260-289: 🩺 Stability & Availability | 🔵 Trivial

Persistent gateway failures silently double AI cost/latency with only console-level visibility.


When AI_GATEWAY_ID is stale/misconfigured, every batch now incurs two Workers AI calls (failing gateway attempt + direct retry) indefinitely, with only a console.warn as signal. Given this is exactly the scenario the new "stale-gateway" test simulates, consider surfacing repeated fallback occurrences via a metric or Sentry breadcrumb/alert so a persistently broken gateway config doesn't go unnoticed for an extended period.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/mcp/capabilities/capability-search.ts` around lines 260 -
289, The fallback in embedTextBatchForVectorize only logs a console warning when
Workers AI Gateway keeps failing, so repeated stale-gateway retries can go
unnoticed. Add a persistent visibility signal for this path by emitting a metric
or Sentry breadcrumb/alert inside the gateway-failure catch before retrying
direct Workers AI, and make sure it includes the gatewayId and error details
from getErrorMessage(error) so the repeated fallback is detectable beyond
console output.

245-293: 🎯 Functional Correctness | 🔵 Trivial | ⚡ Quick win

Add coverage for the gateway + direct failure path. The new combined error message at capability-search.ts:284-286 is still untested; the current tests cover gateway fallback success and direct-only failure, but not the case where both calls reject. A small test that makes both AI.run(...) calls fail would lock this path down.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/mcp/capabilities/capability-search.ts` around lines 245 -
293, Add a test for the combined failure path in embedTextBatchForVectorize:
mock runtime.AI.run to fail first with an AI Gateway gateway.id call and then
fail again on the direct retry, and assert the thrown Error includes both the
gateway and direct error messages. Use the existing runWorkersAiEmbeddingRequest
and embedTextBatchForVectorize symbols to target the same fallback flow the
current tests already cover for success and single-failure cases.
packages/worker/src/mcp/capabilities/capability-search.workers.test.ts (1)

122-157: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Good fallback test; add coverage for combined gateway+direct failure.

Correctly verifies gateway→direct fallback ordering and the warning message. However, per the PR's stated "gateway-to-direct fallback with combined error messages" behavior, there's no test where both the gateway call and the direct fallback call fail — that's the actual failure-surfacing path that matters most for observability.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/mcp/capabilities/capability-search.workers.test.ts`
around lines 122 - 157, Add a test in capability-search.workers.test.ts for the
combined failure path in embedTextsForVectorize: make the first AI Gateway
attempt fail and the direct Workers AI retry fail too, then assert the returned
error surfaces both failures in the combined message. Reuse the existing aiEnv,
embedTextsForVectorize, and CAPABILITY_EMBEDDING_MODEL symbols, and verify the
warning/throw behavior reflects gateway-to-direct fallback followed by final
failure.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@packages/worker/src/mcp/capabilities/reindex-batches.ts`:
- Around line 62-99: The retry path in splitOrRecord/processBatch is re-running
embedding work after an upsert failure, which can both waste calls and
misclassify the error phase. Update the re-split logic in reindex-batches.ts so
that when embedTextsForVectorize succeeds, the computed embeddings are passed
through recursive retries and reused for the split upsert attempts instead of
calling processBatch again. Keep the phase handling in splitOrRecord consistent
by preserving the original 'upsert' failure context while retrying smaller
batches with the same embeddings.

In `@packages/worker/src/package-registry/package-reindex.ts`:
- Around line 39-90: The saved-package reindex flow is accumulating every
manifest load failure in failures, which can make the returned maintenance
payload huge even though failed already tracks the full count. Update the
package reindex logic around loadPackageManifestBySourceId and
reindexVectorCandidates to cap or truncate the stored failure details before
returning them, while still preserving the total failed count and summary.

---

Nitpick comments:
In @.github/workflows/deploy.yml:
- Around line 271-283: The reindex deploy check in the workflow still uses a
hardcoded 120s curl timeout, which may be too short now that the reindex path
can recurse per item and fall back through AI Gateway retries. Review the
`status="$(curl ... --max-time 120 ... __maintenance/reindex-capabilities)"`
step and adjust the timeout strategy to accommodate the longer worst-case
duration, or make it configurable so the `reindex-capabilities` health check
does not fail deploys prematurely.
- Around line 278-279: The workflow step that prints reindex results is exposing
the full reindex-response JSON to CI logs. Update the deploy workflow’s reindex
logging to avoid using the raw cat reindex-response.json output and instead
print only a minimal summary from the response, keeping detailed failure fields
out of the logs unless explicitly needed for debugging. Use the existing reindex
response handling in the deploy job to locate the log statement and replace it
with a summary-focused print.

In `@packages/worker/src/mcp/capabilities/capability-search.ts`:
- Around line 260-289: The fallback in embedTextBatchForVectorize only logs a
console warning when Workers AI Gateway keeps failing, so repeated stale-gateway
retries can go unnoticed. Add a persistent visibility signal for this path by
emitting a metric or Sentry breadcrumb/alert inside the gateway-failure catch
before retrying direct Workers AI, and make sure it includes the gatewayId and
error details from getErrorMessage(error) so the repeated fallback is detectable
beyond console output.
- Around line 245-293: Add a test for the combined failure path in
embedTextBatchForVectorize: mock runtime.AI.run to fail first with an AI Gateway
gateway.id call and then fail again on the direct retry, and assert the thrown
Error includes both the gateway and direct error messages. Use the existing
runWorkersAiEmbeddingRequest and embedTextBatchForVectorize symbols to target
the same fallback flow the current tests already cover for success and
single-failure cases.

In `@packages/worker/src/mcp/capabilities/capability-search.workers.test.ts`:
- Around line 122-157: Add a test in capability-search.workers.test.ts for the
combined failure path in embedTextsForVectorize: make the first AI Gateway
attempt fail and the direct Workers AI retry fail too, then assert the returned
error surfaces both failures in the combined message. Reuse the existing aiEnv,
embedTextsForVectorize, and CAPABILITY_EMBEDDING_MODEL symbols, and verify the
warning/throw behavior reflects gateway-to-direct fallback followed by final
failure.

In `@packages/worker/src/mcp/capabilities/reindex-batches.node.test.ts`:
- Around line 14-98: Add a test in reindex-batches.node.test.ts to cover upsert
failure isolation in reindexVectorCandidates, since the current cases only
verify embedding failures. Mock index.upsert to reject for a batch containing
one bad vector so the helper recursively splits the batch, then assert the
successful vectors are still upserted and embedTextsForVectorize is not called
again during the retry path. Use the existing reindexVectorCandidates,
embedTextsForVectorize, and index.upsert symbols to locate the behavior and keep
the assertions aligned with the current failure-shaping pattern.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro Plus

Run ID: f8acd8a3-f17b-4bb6-801d-73646e1cfccf

📥 Commits

Reviewing files that changed from the base of the PR and between 7fb5290 and dcec519.

📒 Files selected for processing (12)
  • .github/workflows/deploy.yml
  • packages/worker/src/capability-maintenance.node.test.ts
  • packages/worker/src/capability-maintenance.ts
  • packages/worker/src/jobs/job-reindex.ts
  • packages/worker/src/mcp/capabilities/capability-reindex.ts
  • packages/worker/src/mcp/capabilities/capability-search.ts
  • packages/worker/src/mcp/capabilities/capability-search.workers.test.ts
  • packages/worker/src/mcp/capabilities/reindex-batches.node.test.ts
  • packages/worker/src/mcp/capabilities/reindex-batches.ts
  • packages/worker/src/mcp/memory/memory-reindex.ts
  • packages/worker/src/package-registry/package-reindex.node.test.ts
  • packages/worker/src/package-registry/package-reindex.ts

Comment on lines +62 to +99
async function splitOrRecord(
batch: ReadonlyArray<VectorReindexCandidate>,
phase: VectorReindexFailurePhase,
error: unknown,
) {
if (batch.length === 1) {
recordFailure({ candidate: batch[0]!, phase, error })
return
}
const midpoint = Math.ceil(batch.length / 2)
await processBatch(batch.slice(0, midpoint))
await processBatch(batch.slice(midpoint))
}

async function processBatch(batch: ReadonlyArray<VectorReindexCandidate>) {
let embeddings: Array<Array<number>>
try {
embeddings = await embedTextsForVectorize(
input.env,
batch.map((candidate) => candidate.text),
)
} catch (error) {
await splitOrRecord(batch, 'embed', error)
return
}

try {
await input.index.upsert(
batch.map((candidate, index_) => ({
id: candidate.id,
values: embeddings[index_]!,
metadata: candidate.metadata,
})),
)
upserted += batch.length
} catch (error) {
await splitOrRecord(batch, 'upsert', error)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Reuse computed embeddings when splitting upsert failures.

After embedding succeeds, the upsert catch path calls splitOrRecord(), which re-enters processBatch() and embeds each split again. A transient AI/Gateway failure during that retry can misclassify an upsert problem as an embed failure and adds unnecessary embedding calls.

Suggested direction
+type EmbeddedVectorCandidate = {
+	candidate: VectorReindexCandidate
+	values: Array<number>
+}
+
-async function splitOrRecord(
+async function splitEmbedOrRecord(
 	batch: ReadonlyArray<VectorReindexCandidate>,
-	phase: VectorReindexFailurePhase,
 	error: unknown,
 ) {
 	if (batch.length === 1) {
-		recordFailure({ candidate: batch[0]!, phase, error })
+		recordFailure({ candidate: batch[0]!, phase: 'embed', error })
 		return
 	}
 	const midpoint = Math.ceil(batch.length / 2)
 	await processBatch(batch.slice(0, midpoint))
 	await processBatch(batch.slice(midpoint))
 }
+
+async function upsertEmbeddedBatch(batch: ReadonlyArray<EmbeddedVectorCandidate>) {
+	try {
+		await input.index.upsert(
+			batch.map(({ candidate, values }) => ({
+				id: candidate.id,
+				values,
+				metadata: candidate.metadata,
+			})),
+		)
+		upserted += batch.length
+	} catch (error) {
+		if (batch.length === 1) {
+			recordFailure({ candidate: batch[0]!.candidate, phase: 'upsert', error })
+			return
+		}
+		const midpoint = Math.ceil(batch.length / 2)
+		await upsertEmbeddedBatch(batch.slice(0, midpoint))
+		await upsertEmbeddedBatch(batch.slice(midpoint))
+	}
+}
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/mcp/capabilities/reindex-batches.ts` around lines 62 -
99, The retry path in splitOrRecord/processBatch is re-running embedding work
after an upsert failure, which can both waste calls and misclassify the error
phase. Update the re-split logic in reindex-batches.ts so that when
embedTextsForVectorize succeeds, the computed embeddings are passed through
recursive retries and reused for the split upsert attempts instead of calling
processBatch again. Keep the phase handling in splitOrRecord consistent by
preserving the original 'upsert' failure context while retrying smaller batches
with the same embeddings.

Comment on lines +39 to +90
const failures: Array<VectorReindexFailure> = []
const candidates: Array<VectorReindexCandidate> = []
for (const row of rows) {
try {
const { manifest } = await loadPackageManifestBySourceId({
env,
baseUrl: input.baseUrl,
userId: row.userId,
sourceId: row.sourceId,
})
candidates.push({
id: savedPackageVectorId(row.id),
values: vectors[index_]!,
text: buildSavedPackageEmbedText(manifest),
metadata: {
kind: 'package',
userId: row.userId,
},
})),
)
upserted += loaded.length
})
} catch (error) {
failed += 1
const failure = {
id: savedPackageVectorId(row.id),
phase: 'load' as const,
error: getErrorMessage(error),
}
failures.push(failure)
console.error(
JSON.stringify({
message: 'saved package vector reindex skipped package',
packageId: row.id,
sourceId: row.sourceId,
error: failure.error,
}),
)
}
}

return failed > 0
? {
upserted,
failed,
error: `${failed} saved package vector(s) failed to reindex`,
}
: { upserted }
const result = await reindexVectorCandidates({
env,
index,
kind: 'saved package',
candidates,
})
const totalFailed = failed + (result.failed ?? 0)
const allFailures = [...failures, ...(result.failures ?? [])]
if (totalFailed === 0) return { upserted: result.upserted }

const summary = `${totalFailed} saved package vector(s) failed to reindex`
return {
upserted: result.upserted,
failed: totalFailed,
failures: allFailures,

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Cap load-phase failure details before returning them.

reindexVectorCandidates() caps reported failures, but manifest load failures are accumulated unbounded here. If many saved packages fail to load, the maintenance response can become very large even though failed already carries the full count.

Suggested fix
 const failures: Array<VectorReindexFailure> = []
+const maxReportedFailures = 20
 const candidates: Array<VectorReindexCandidate> = []
@@
-			failures.push(failure)
+			if (failures.length < maxReportedFailures) failures.push(failure)
@@
-	const allFailures = [...failures, ...(result.failures ?? [])]
+	const allFailures = [...failures, ...(result.failures ?? [])].slice(
+		0,
+		maxReportedFailures,
+	)
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const failures: Array<VectorReindexFailure> = []
const candidates: Array<VectorReindexCandidate> = []
for (const row of rows) {
try {
const { manifest } = await loadPackageManifestBySourceId({
env,
baseUrl: input.baseUrl,
userId: row.userId,
sourceId: row.sourceId,
})
candidates.push({
id: savedPackageVectorId(row.id),
values: vectors[index_]!,
text: buildSavedPackageEmbedText(manifest),
metadata: {
kind: 'package',
userId: row.userId,
},
})),
)
upserted += loaded.length
})
} catch (error) {
failed += 1
const failure = {
id: savedPackageVectorId(row.id),
phase: 'load' as const,
error: getErrorMessage(error),
}
failures.push(failure)
console.error(
JSON.stringify({
message: 'saved package vector reindex skipped package',
packageId: row.id,
sourceId: row.sourceId,
error: failure.error,
}),
)
}
}
return failed > 0
? {
upserted,
failed,
error: `${failed} saved package vector(s) failed to reindex`,
}
: { upserted }
const result = await reindexVectorCandidates({
env,
index,
kind: 'saved package',
candidates,
})
const totalFailed = failed + (result.failed ?? 0)
const allFailures = [...failures, ...(result.failures ?? [])]
if (totalFailed === 0) return { upserted: result.upserted }
const summary = `${totalFailed} saved package vector(s) failed to reindex`
return {
upserted: result.upserted,
failed: totalFailed,
failures: allFailures,
const failures: Array<VectorReindexFailure> = []
const maxReportedFailures = 20
const candidates: Array<VectorReindexCandidate> = []
for (const row of rows) {
try {
const { manifest } = await loadPackageManifestBySourceId({
env,
baseUrl: input.baseUrl,
userId: row.userId,
sourceId: row.sourceId,
})
candidates.push({
id: savedPackageVectorId(row.id),
text: buildSavedPackageEmbedText(manifest),
metadata: {
kind: 'package',
userId: row.userId,
},
})
} catch (error) {
failed += 1
const failure = {
id: savedPackageVectorId(row.id),
phase: 'load' as const,
error: getErrorMessage(error),
}
if (failures.length < maxReportedFailures) failures.push(failure)
console.error(
JSON.stringify({
message: 'saved package vector reindex skipped package',
packageId: row.id,
sourceId: row.sourceId,
error: failure.error,
}),
)
}
}
const result = await reindexVectorCandidates({
env,
index,
kind: 'saved package',
candidates,
})
const totalFailed = failed + (result.failed ?? 0)
const allFailures = [...failures, ...(result.failures ?? [])].slice(
0,
maxReportedFailures,
)
if (totalFailed === 0) return { upserted: result.upserted }
const summary = `${totalFailed} saved package vector(s) failed to reindex`
return {
upserted: result.upserted,
failed: totalFailed,
failures: allFailures,
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@packages/worker/src/package-registry/package-reindex.ts` around lines 39 -
90, The saved-package reindex flow is accumulating every manifest load failure
in failures, which can make the returned maintenance payload huge even though
failed already tracks the full count. Update the package reindex logic around
loadPackageManifestBySourceId and reindexVectorCandidates to cap or truncate the
stored failure details before returning them, while still preserving the total
failed count and summary.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants