@@ -64,7 +64,7 @@ import {
6464 mergeDurableSecretProvenance ,
6565} from '@/lib/execution/durable-secret-provenance'
6666import { processDocument } from '@/lib/knowledge/documents/document-processor'
67- import { KNOWLEDGE_DOCUMENT_PROCESSING_STALE_THRESHOLD_MS } from '@/lib/knowledge/documents/processing-claim'
67+ import { failStaleDocumentProcessingClaim } from '@/lib/knowledge/documents/processing-claim'
6868import { enqueueKnowledgeDocumentProcessing } from '@/lib/knowledge/documents/processing-outbox-event'
6969import {
7070 assertDocumentProcessingBillingContext ,
@@ -118,6 +118,13 @@ import { calculateCost } from '@/providers/utils'
118118
119119const logger = createLogger ( 'DocumentService' )
120120
121+ class DocumentProcessingClaimUnavailableError extends Error {
122+ constructor ( documentId : string ) {
123+ super ( `Knowledge document ${ documentId } processing claim is owned by another attempt` )
124+ this . name = 'DocumentProcessingClaimUnavailableError'
125+ }
126+ }
127+
121128/**
122129 * Thrown when a knowledge-base document's `fileUrl` references an internal
123130 * knowledge-base storage object not owned by the target knowledge base's workspace.
@@ -881,7 +888,7 @@ export async function processDocumentAsync(
881888 logger . info ( `[${ documentId } ] Skipping document processing because another attempt owns it` , {
882889 processingStatus : ctx . processingStatus ,
883890 } )
884- return
891+ throw new DocumentProcessingClaimUnavailableError ( documentId )
885892 }
886893
887894 logger . info ( `[${ documentId } ] Status updated to 'processing', starting document processor` )
@@ -1236,6 +1243,10 @@ export async function processDocumentAsync(
12361243 fileSize : docData . fileSize ,
12371244 } )
12381245
1246+ if ( error instanceof DocumentProcessingClaimUnavailableError ) {
1247+ throw error
1248+ }
1249+
12391250 await db
12401251 . update ( document )
12411252 . set ( {
@@ -2371,30 +2382,17 @@ export async function markDocumentAsFailedTimeout(
23712382 processingStartedAt : Date ,
23722383 requestId : string
23732384) : Promise < { success : boolean ; processingDuration : number } > {
2374- const now = new Date ( )
2375- const processingDuration = now . getTime ( ) - processingStartedAt . getTime ( )
2385+ const result = await failStaleDocumentProcessingClaim ( { documentId, processingStartedAt } )
23762386
2377- if ( processingDuration <= KNOWLEDGE_DOCUMENT_PROCESSING_STALE_THRESHOLD_MS ) {
2378- throw new Error ( 'Document has not been processing long enough to be considered dead' )
2387+ if ( result . success ) {
2388+ logger . info (
2389+ `[${ requestId } ] Marked document ${ documentId } as failed due to dead process (processing time: ${ Math . round ( result . processingDuration / 1000 ) } s)`
2390+ )
2391+ } else {
2392+ logger . info ( `[${ requestId } ] Did not time out document ${ documentId } because its claim changed` )
23792393 }
23802394
2381- await db
2382- . update ( document )
2383- . set ( {
2384- processingStatus : 'failed' ,
2385- processingError : 'Processing timed out. Please retry or re-sync the connector.' ,
2386- processingCompletedAt : now ,
2387- } )
2388- . where ( eq ( document . id , documentId ) )
2389-
2390- logger . info (
2391- `[${ requestId } ] Marked document ${ documentId } as failed due to dead process (processing time: ${ Math . round ( processingDuration / 1000 ) } s)`
2392- )
2393-
2394- return {
2395- success : true ,
2396- processingDuration,
2397- }
2395+ return result
23982396}
23992397
24002398export async function retryDocumentProcessing (
0 commit comments