Skip to content

Hotfix - Rag reingest - #19753

Merged
adrinr merged 11 commits into
cloudfrom
hotfix/rag-reingest
Sep 28, 2026
Merged

adrinr merged 11 commits into
cloudfrom
hotfix/rag-reingest

Conversation

@adrinr

@adrinr adrinr commented Sep 28, 2026 •

Copy link
Copy Markdown
Member

Description

Hotfix bringing these 2 PRs:


Summary by cubic

Hotfix that ships two fixes: bulk row deletion now rejects rows that don't belong to the requested table, and Gemini knowledge base ingestion surfaces a store reset action on the first access failure.

  • Bulk row deletion validates each row's table against the requested table and returns a 500 on mismatch.
  • RAG ingestion treats Gemini store 403/404 errors as non-retryable, marks the file as failed, and discards the job so the reset button appears immediately.
  • Retries continue only for temporary HTTP failures (429, 503) and stop after the configured attempt limit.

Written for commit c3954f7. Summary will update on new commits.

Review in cubic

@adrinr
adrinr enabled auto-merge September 28, 2026 14:25
@adrinr
adrinr disabled auto-merge September 28, 2026 14:25

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

5 issues found and verified against the latest diff

Confidence score: 3/5

  • geminiFileStore.ts treats every LiteLLM 403/404 as a Gemini store failure, which can give users misleading reset guidance for unrelated auth or route errors; show that guidance only when the response identifies a fileSearchStores failure.
  • ragQueue.ts does not recognize S3 object-store status fields, so missing or inaccessible uploads can consume all retries; normalize those status fields. It also drops .message from thrown non-Error objects, losing actionable persisted failure details; preserve object messages.
  • internal.ts throws a plain string for the table ID mismatch, so Koa returns a generic 500 and API clients cannot surface the intended message; throw an HTTPError instead.
  • ragQueue.spec.ts does not exercise the retry behavior configured in getQueue() because the queue is mocked and makeJob hardcodes attempts; add coverage that verifies the configured retry behavior.
Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name="packages/server/src/sdk/workspace/ai/rag/ragQueue.ts">

<violation number="1" location="packages/server/src/sdk/workspace/ai/rag/ragQueue.ts:215">
P2: S3 object-store 403/404 failures do not satisfy this `HTTPError`-only check. Normalize the object-store status fields as well, otherwise missing or inaccessible uploads still undergo all retries instead of taking the permanent-failure path.</violation>

<violation number="2" location="packages/server/src/sdk/workspace/ai/rag/ragQueue.ts:217">
P3: `errorMessage` now discards `.message` from thrown non-`Error` objects, losing actionable failure details that the previous optional-property read preserved. Keep object messages when constructing the persisted error text.</violation>
</file>

<file name="packages/server/src/api/controllers/row/internal.ts">

<violation number="1" location="packages/server/src/api/controllers/row/internal.ts:165">
P2: This new mismatch guard throws a plain string, which koa turns into a generic 500 without the message, so API clients can't surface "Supplied tableId doesn't match...". Throw `new HTTPError("Supplied tableId doesn't match the row's tableId", 400)` instead (HTTPError is already imported in this file).</violation>
</file>

<file name="packages/server/src/sdk/workspace/ai/knowledgeBase/geminiFileStore.ts">

<violation number="1" location="packages/server/src/sdk/workspace/ai/knowledgeBase/geminiFileStore.ts:242">
P2: This maps every 403/404 from LiteLLM `/v1/rag/ingest` to a Gemini store failure, including unrelated LiteLLM auth or route errors. Only show the reset guidance when the response identifies a `fileSearchStores` failure; otherwise preserve the upstream error.</violation>
</file>

<file name="packages/server/src/sdk/workspace/ai/rag/ragQueue.spec.ts">

<violation number="1" location="packages/server/src/sdk/workspace/ai/rag/ragQueue.spec.ts:64">
P3: The retry behavior this hotfix restores lives in getQueue()'s jobOptions (attempts: 5, exponential backoff), but these tests never exercise it: BudibaseQueue is mocked away and makeJob hardcodes opts.attempts: 5, duplicating the production constant. A regression that drops jobOptions.attempts back to 1 would leave both "keeps retrying HTTP" tests green, so the test suite would not catch the very bug the hotfix fixes. Keep the processor unit tests, but also pin the retry configuration to the real queue, e.g. assert getQueue() registers attempts 5 with exponential backoff, or build the test job's opts from the queue's jobOptions instead of hardcoding them.</violation>
</file>

Tip: instead of fixing issues one by one fix them all with cubic

Re-trigger cubic

const attempts = job.opts.attempts || 1
const isFinalAttempt = job.attemptsMade + 1 >= attempts
const isStoreAccessError =
error instanceof HTTPError && (error.status === 403 || error.status === 404)

@cubic-dev-ai cubic-dev-ai Bot Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: S3 object-store 403/404 failures do not satisfy this HTTPError-only check. Normalize the object-store status fields as well, otherwise missing or inaccessible uploads still undergo all retries instead of taking the permanent-failure path.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/server/src/sdk/workspace/ai/rag/ragQueue.ts, line 215:

<comment>S3 object-store 403/404 failures do not satisfy this `HTTPError`-only check. Normalize the object-store status fields as well, otherwise missing or inaccessible uploads still undergo all retries instead of taking the permanent-failure path.</comment>

<file context>
@@ -194,31 +200,42 @@ const loadFileBuffer = async (objectKey: string): Promise<Buffer> => {
   const attempts = job.opts.attempts || 1
   const isFinalAttempt = job.attemptsMade + 1 >= attempts
+  const isStoreAccessError =
+    error instanceof HTTPError && (error.status === 403 || error.status === 404)
+  const errorMessage =
+    error instanceof Error ? error.message : "Failed to process uploaded file"
</file context>
Fix with cubic

rows.some((row: Row) => row.tableId && row.tableId !== tableId) ||
dbRows.some(row => row.tableId !== tableId)
) {
throw "Supplied tableId doesn't match the row's tableId"

@cubic-dev-ai cubic-dev-ai Bot Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: This new mismatch guard throws a plain string, which koa turns into a generic 500 without the message, so API clients can't surface "Supplied tableId doesn't match...". Throw new HTTPError("Supplied tableId doesn't match the row's tableId", 400) instead (HTTPError is already imported in this file).

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/server/src/api/controllers/row/internal.ts, line 165:

<comment>This new mismatch guard throws a plain string, which koa turns into a generic 500 without the message, so API clients can't surface "Supplied tableId doesn't match...". Throw `new HTTPError("Supplied tableId doesn't match the row's tableId", 400)` instead (HTTPError is already imported in this file).</comment>

<file context>
@@ -152,6 +152,18 @@ export async function bulkDestroy(ctx: UserCtx) {
+    rows.some((row: Row) => row.tableId && row.tableId !== tableId) ||
+    dbRows.some(row => row.tableId !== tableId)
+  ) {
+    throw "Supplied tableId doesn't match the row's tableId"
+  }
 
</file context>
Suggested change
throw "Supplied tableId doesn't match the row's tableId"
throw new HTTPError("Supplied tableId doesn't match the row's tableId", 400)
Fix with cubic

})

if (!response.ok) {
throwIfStoreAccessError(response.status)

@cubic-dev-ai cubic-dev-ai Bot Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P2: This maps every 403/404 from LiteLLM /v1/rag/ingest to a Gemini store failure, including unrelated LiteLLM auth or route errors. Only show the reset guidance when the response identifies a fileSearchStores failure; otherwise preserve the upstream error.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/server/src/sdk/workspace/ai/knowledgeBase/geminiFileStore.ts, line 242:

<comment>This maps every 403/404 from LiteLLM `/v1/rag/ingest` to a Gemini store failure, including unrelated LiteLLM auth or route errors. Only show the reset guidance when the response identifies a `fileSearchStores` failure; otherwise preserve the upstream error.</comment>

<file context>
@@ -223,6 +238,10 @@ export async function ingestGeminiFile({
   })
 
+  if (!response.ok) {
+    throwIfStoreAccessError(response.status)
+  }
+
</file context>
Fix with cubic

const isStoreAccessError =
error instanceof HTTPError && (error.status === 403 || error.status === 404)
const errorMessage =
error instanceof Error ? error.message : "Failed to process uploaded file"

@cubic-dev-ai cubic-dev-ai Bot Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3: errorMessage now discards .message from thrown non-Error objects, losing actionable failure details that the previous optional-property read preserved. Keep object messages when constructing the persisted error text.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/server/src/sdk/workspace/ai/rag/ragQueue.ts, line 217:

<comment>`errorMessage` now discards `.message` from thrown non-`Error` objects, losing actionable failure details that the previous optional-property read preserved. Keep object messages when constructing the persisted error text.</comment>

<file context>
@@ -194,31 +200,42 @@ const loadFileBuffer = async (objectKey: string): Promise<Buffer> => {
+  const isStoreAccessError =
+    error instanceof HTTPError && (error.status === 403 || error.status === 404)
+  const errorMessage =
+    error instanceof Error ? error.message : "Failed to process uploaded file"
 
-  if (!isFinalAttempt) {
</file context>
Suggested change
error instanceof Error ? error.message : "Failed to process uploaded file"
error instanceof Error
? error.message
: typeof error === "object" && error !== null && "message" in error
? String(error.message)
: "Failed to process uploaded file"
Fix with cubic

knowledgeBaseId: "kb-1",
fileId: "file-1",
},
opts: { attempts: 5, timeout: 600_000 },

@cubic-dev-ai cubic-dev-ai Bot Sep 28, 2026 •

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P3: The retry behavior this hotfix restores lives in getQueue()'s jobOptions (attempts: 5, exponential backoff), but these tests never exercise it: BudibaseQueue is mocked away and makeJob hardcodes opts.attempts: 5, duplicating the production constant. A regression that drops jobOptions.attempts back to 1 would leave both "keeps retrying HTTP" tests green, so the test suite would not catch the very bug the hotfix fixes. Keep the processor unit tests, but also pin the retry configuration to the real queue, e.g. assert getQueue() registers attempts 5 with exponential backoff, or build the test job's opts from the queue's jobOptions instead of hardcoding them.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At packages/server/src/sdk/workspace/ai/rag/ragQueue.spec.ts, line 64:

<comment>The retry behavior this hotfix restores lives in getQueue()'s jobOptions (attempts: 5, exponential backoff), but these tests never exercise it: BudibaseQueue is mocked away and makeJob hardcodes opts.attempts: 5, duplicating the production constant. A regression that drops jobOptions.attempts back to 1 would leave both "keeps retrying HTTP" tests green, so the test suite would not catch the very bug the hotfix fixes. Keep the processor unit tests, but also pin the retry configuration to the real queue, e.g. assert getQueue() registers attempts 5 with exponential backoff, or build the test job's opts from the queue's jobOptions instead of hardcoding them.</comment>

<file context>
@@ -0,0 +1,154 @@
+        knowledgeBaseId: "kb-1",
+        fileId: "file-1",
+      },
+      opts: { attempts: 5, timeout: 600_000 },
+      attemptsMade,
+      discard: jest.fn(),
</file context>
Fix with cubic

@adrinr
adrinr enabled auto-merge September 28, 2026 14:40
@adrinr
adrinr disabled auto-merge September 28, 2026 14:45
@adrinr
adrinr merged commit da01756 into cloud Sep 28, 2026
121 of 129 checks passed
@adrinr
adrinr deleted the hotfix/rag-reingest branch September 28, 2026 14:47
@github-actions github-actions Bot locked and limited conversation to collaborators Sep 28, 2026
Sign up for free to subscribe to this conversation on GitHub. Already have an account? Sign in.

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants

Sponsor
SponsoredKunjungi sekarang
Promo