Repository navigation
Hotfix - Rag reingest - #19753
Hotfix - Rag reingest#19753
Conversation
Co-authored-by: cubic-dev-ai[bot] <191113872+cubic-dev-ai[bot]@users.noreply.github.com>
Merge Cloud changes back to master
…workspace-document-deletion-through-the-bulk-row-endpoint fix: Guard batch row deletions
fix: restore store button at first fail
There was a problem hiding this comment.
5 issues found and verified against the latest diff
Confidence score: 3/5
geminiFileStore.tstreats 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 afileSearchStoresfailure.ragQueue.tsdoes not recognize S3 object-store status fields, so missing or inaccessible uploads can consume all retries; normalize those status fields. It also drops.messagefrom thrown non-Errorobjects, losing actionable persisted failure details; preserve object messages.internal.tsthrows a plain string for the table ID mismatch, so Koa returns a generic 500 and API clients cannot surface the intended message; throw anHTTPErrorinstead.ragQueue.spec.tsdoes not exercise the retry behavior configured ingetQueue()because the queue is mocked andmakeJobhardcodes 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) |
There was a problem hiding this comment.
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>
| 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" |
There was a problem hiding this comment.
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>
| throw "Supplied tableId doesn't match the row's tableId" | |
| throw new HTTPError("Supplied tableId doesn't match the row's tableId", 400) |
| }) | ||
|
|
||
| if (!response.ok) { | ||
| throwIfStoreAccessError(response.status) |
There was a problem hiding this comment.
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>
| const isStoreAccessError = | ||
| error instanceof HTTPError && (error.status === 403 || error.status === 404) | ||
| const errorMessage = | ||
| error instanceof Error ? error.message : "Failed to process uploaded file" |
There was a problem hiding this comment.
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>
| 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" |
| knowledgeBaseId: "kb-1", | ||
| fileId: "file-1", | ||
| }, | ||
| opts: { attempts: 5, timeout: 600_000 }, |
There was a problem hiding this comment.
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>
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.
Written for commit c3954f7. Summary will update on new commits.