{"record":{"id":"0c16f5c255724a19","repo":"weaviate/weaviate","slug":"no-export-prepared","errorCode":null,"errorMessage":"no export prepared","messagePattern":"no export prepared","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"usecases/export/participant.go","lineNumber":330,"sourceCode":"\t\t\t\tp.clearAndRelease()\n\t\t\t}\n\t\t\tp.mu.Unlock()\n\t\t}()\n\n\t\ttimer := p.abortTimer\n\t\tif timer == nil {\n\t\t\terrRet = fmt.Errorf(\"timer is nil. No export prepared\")\n\t\t\treturn errRet\n\t\t}\n\t\ttimer.Stop()\n\n\t\tif p.activeExport != exportID {\n\t\t\terrRet = fmt.Errorf(\"active export ID mismatch: expected %q, got %q\", p.activeExport, exportID)\n\t\t\treturn errRet\n\t\t}\n\n\t\tif p.preparedReq == nil {\n\t\t\terrRet = fmt.Errorf(\"no export prepared\")\n\t\t\treturn errRet\n\t\t}\n\t\tif p.preparedReq.ID != exportID {\n\t\t\terrRet = fmt.Errorf(\"export ID mismatch: expected %q, got %q\", p.preparedReq.ID, exportID)\n\t\t\treturn errRet\n\t\t}\n\t\t// Pointer identity check: if an Abort cleared the slot and a new\n\t\t// Prepare set a different *ExportRequest (possibly with the same\n\t\t// export ID) while we were doing backend I/O outside the lock,\n\t\t// the pointer will differ even though the ID matches. This is a\n\t\t// defense-in-depth guard — the coordinator prevents ID reuse via\n\t\t// checkIfExportExists, so this race cannot happen in practice.\n\t\tif p.preparedReq != req {\n\t\t\terrRet = fmt.Errorf(\"export request was replaced during backend initialization (abort+re-prepare race)\")\n\t\t\treturn errRet\n\t\t}\n\n\t\tif backendStore == nil {","sourceCodeStart":312,"sourceCodeEnd":348,"githubUrl":"https://github.com/weaviate/weaviate/blob/75aa4b6d11f8818305aafd4440b4e32794f7ca04/usecases/export/participant.go#L312-L348","documentation":"Commit's critical section found p.preparedReq nil even though activeExport still matches. This means the prepared-request slot was cleared (by a timeout callback, Abort, or a prior Commit) while the activeExport marker was not yet reset — an inconsistent intermediate state caught defensively before dereferencing preparedReq.","triggerScenarios":"The abortTimer callback ran clearAndRelease concurrently; a previous Commit attempt errored after nil-ing preparedReq; Abort racing Commit between the unlocked peek and the locked critical section.","commonSituations":"Slow snapshot waits or backend initialization widening the race window; tests killing timers or forcing timeouts.","solutions":["Restart the export lifecycle with a new Prepare — the reservation is gone and cannot be recovered.","Check for concurrent Abort/timeout activity during Commit; serialize operations on the participant.","Shorten work performed between the unlocked state peek and Commit's critical section."],"exampleFix":"// before\n// concurrent Abort clears preparedReq mid-Commit -> \"no export prepared\"\ngo participant.Abort(id)\nerr := participant.Commit(ctx, id)\n// after\n// serialize lifecycle calls\nmu.Lock()\nerr := participant.Commit(ctx, id)\nmu.Unlock()","handlingStrategy":"retry","validationCode":"// do not call Abort concurrently with Commit for the same ID\n// (serialize lifecycle calls with your own mutex)","typeGuard":null,"tryCatchPattern":"if err := participant.Commit(ctx, id); err != nil {\n    if strings.Contains(err.Error(), \"no export prepared\") {\n        return retryWithFreshPrepare(ctx) // slot cleared; fresh lifecycle required\n    }\n    return err\n}","preventionTips":["Serialize Prepare/Commit/Abort per participant — these states are not safe to interleave.","Expect this after any timeout or Abort; the reservation is gone and only a fresh Prepare restores it.","Treat every non-retryable state error as terminal: never loop Commit against the same ID."],"tags":["export","state-machine","race-condition"],"backgroundTag":"no-export-prepared","analyzedSha":"75aa4b6d11f8818305aafd4440b4e32794f7ca04","analyzedAt":"2026-09-04T14:58:20.392Z","contentChangedAt":"2026-09-04T14:58:20.392Z","schemaVersion":2},"datasetVersion":"2026-09-11T21:17:09.523Z"}