OUT-3804: reconcile early-stamped assemblyFileId on create retry#110
Conversation
retryCreateInAssembly now checks the existing Assembly file before re-creating, so a retry can't orphan it or produce a duplicate: completed -> mark synced; pending & young -> wait; pending & stale -> null id, delete, re-create; 404 -> re-create. Extracted the block into reconcileExistingAssemblyFile, hoisted STUCK_PENDING_THRESHOLD_MS, and shared the channelSync lookup. Added unit tests for each branch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
Greptile SummaryThis PR fixes a sweeper retry bug where an early-stamped
Confidence Score: 3/5Safe to merge for the common cases, but the age-gate in reconcileExistingAssemblyFile silently swallows any non-completed, non-pending Assembly file status — worth confirming the API's full status surface before landing. The reconcile logic only explicitly handles src/features/workers/resync-failed-files/helper/resync-failed-files.helper.ts — specifically the reconcileExistingAssemblyFile function's post-completed fallthrough. Important Files Changed
Flowchart%%{init: {'theme': 'neutral'}}%%
flowchart TD
A[retryCreateInAssembly] --> B{dbxFileId / itemPath / channelSync present?}
B -- No --> Z[markFailure / markDeleted]
B -- Yes --> C{Dropbox file exists?}
C -- No / deleted --> D[markDeleted]
C -- Yes --> E[reconcileExistingAssemblyFile]
E --> F{assemblyFileId present?}
F -- No --> G[return true]
F -- Yes --> H[retrieveFile]
H -- completed --> I[markUpdated + updateChannelMapSyncedFilesCount
return false]
H -- other status --> J{row age < 24h?}
J -- Yes --> K[markFailure 'still pending'
return false]
J -- No --> L[updateFileMap assemblyFileId=null
deleteFile
return true]
H -- 404 --> G
G --> M[completePendingAssemblyCreate]
Reviews (1): Last reviewed commit: "fix(OUT-3804): reconcile early-stamped a..." | Re-trigger Greptile |
| }) | ||
| return true | ||
| } catch (error) { | ||
| if (!isCopilotApiError(error) || error.status !== 404) { |
There was a problem hiding this comment.
Should this be || or &&?
My understanding is if 404, then it means file does not exist, right? But I think that should go to .catch in line 274.
There was a problem hiding this comment.
It's ||. This basically means if the error is not related to copilotError or the request responded with 404 not found we assume the file is not available and we return true.
Will update this try catch block. Thanks for the suggestion
| if (!failedSync.assemblyFileId) return true | ||
|
|
||
| try { | ||
| const existing = await copilotApi.retrieveFile(failedSync.assemblyFileId) |
There was a problem hiding this comment.
Better to return true early, if existing is not defined, etc.
| const existing = await copilotApi.retrieveFile(failedSync.assemblyFileId) | |
| const existing = await copilotApi.retrieveFile(failedSync.assemblyFileId).catch((e: AxiosError) => es.status == 404 ? null : throw e) | |
| if (!existing) return true |
There was a problem hiding this comment.
Will update this try catch block. Thanks for the suggestion.
| 'retryCreateInAssembly: Assembly file still pending upload, will retry next sweep', | ||
| ) | ||
| return false | ||
| } |
There was a problem hiding this comment.
Should it be retried though? If upload is in progress and this request did not intiate it, then retry would be processed by the original request that initiated the upload, wouldn't it?
There was a problem hiding this comment.
Yes it should be retried. The original request retry for 3 attempts and if the attempt still fails (due to uploads failure), the status is always on pending. We retry and check if status is pending for more than a day. If so we delete that pending record from assembly and create new one and upload again.
Resolve the early-stamped assemblyFileId 404 case at the retrieveFile call so a missing file routes to the create path, while non-404 Copilot errors propagate for retry instead of being swallowed. Removes the now pass-through try/catch. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
What
retryCreateInAssemblynow reconciles a row's early-stampedassemblyFileIdbefore re-creating, so a sweeper retry can't orphan the existing Assembly file or produce a duplicate.Branches on the existing Assembly file's state:
completedRefactor
reconcileExistingAssemblyFile(returns a "should re-create" boolean).STUCK_PENDING_THRESHOLD_MS.channelSynclookups intogetChannelSync.Race safety
The stale-reclaim path nulls
assemblyFileIdbefore the API delete, so the resultingfile.deletedwebhook can't match this row and race the re-create. A unit test pins this ordering viainvocationCallOrder.Tests
New
resync-failed-files.helper.test.tscovers all reconcile branches (completed / pending-young / pending-stale / 404 / no-id / source-gone). The 404 test throws a properly-shapedCopilotApiErrorand uses the real guard logic, so it actually exercises the productionisCopilotApiErrorcheck.pnpm typecheck,pnpm lint, and the resync + tombstone vitest suites all pass.Related: OUT-3645
🤖 Generated with Claude Code