weaviate/weaviate · error
no export prepared
Error message
no export prepared
What it means
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.
Source
Thrown at usecases/export/participant.go:330
p.clearAndRelease()
}
p.mu.Unlock()
}()
timer := p.abortTimer
if timer == nil {
errRet = fmt.Errorf("timer is nil. No export prepared")
return errRet
}
timer.Stop()
if p.activeExport != exportID {
errRet = fmt.Errorf("active export ID mismatch: expected %q, got %q", p.activeExport, exportID)
return errRet
}
if p.preparedReq == nil {
errRet = fmt.Errorf("no export prepared")
return errRet
}
if p.preparedReq.ID != exportID {
errRet = fmt.Errorf("export ID mismatch: expected %q, got %q", p.preparedReq.ID, exportID)
return errRet
}
// Pointer identity check: if an Abort cleared the slot and a new
// Prepare set a different *ExportRequest (possibly with the same
// export ID) while we were doing backend I/O outside the lock,
// the pointer will differ even though the ID matches. This is a
// defense-in-depth guard — the coordinator prevents ID reuse via
// checkIfExportExists, so this race cannot happen in practice.
if p.preparedReq != req {
errRet = fmt.Errorf("export request was replaced during backend initialization (abort+re-prepare race)")
return errRet
}
if backendStore == nil {View on GitHub (pinned to 75aa4b6d11)
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.
Example fix
// before // concurrent Abort clears preparedReq mid-Commit -> "no export prepared" go participant.Abort(id) err := participant.Commit(ctx, id) // after // serialize lifecycle calls mu.Lock() err := participant.Commit(ctx, id) mu.Unlock()
Defensive patterns
Strategy: retry
Validate before calling
// do not call Abort concurrently with Commit for the same ID // (serialize lifecycle calls with your own mutex)
Try / catch
if err := participant.Commit(ctx, id); err != nil {
if strings.Contains(err.Error(), "no export prepared") {
return retryWithFreshPrepare(ctx) // slot cleared; fresh lifecycle required
}
return err
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: Slow snapshot waits or backend initialization widening the race window; tests killing timers or forcing timeouts.
Related errors
- active export ID mismatch: expected %q, got %q
- export has already finished
- no matching export prepared for ID %q
- timer is nil. No export prepared
- export ID mismatch: expected %q, got %q
AI-assisted analysis of weaviate/weaviate@75aa4b6d11 (2026-09-04).
Data as JSON: /api/errors/0c16f5c255724a19.
Report an issue: GitHub.