benbjohnson/litestream · error
shutdown sync timeout after %d attempts: %w
Error message
shutdown sync timeout after %d attempts: %w
What it means
syncReplicaWithRetry retries the final replication on DB.Close until a deadline context expires; when deadlineCtx.Done() fires it returns "shutdown sync timeout after N attempts" wrapping the last replication error. This means the final sync could not complete within the configured shutdown timeout despite N retries, and the last underlying error is preserved.
Source
Thrown at db.go:960
if err := db.Replica.Sync(syncCtx); err == nil {
if attempt > 1 {
db.Logger.Info("shutdown sync succeeded after retry",
"attempts", attempt,
"duration", time.Since(startTime))
}
return nil
} else {
lastErr = err
}
// Check if we should stop retrying (done signal or timeout)
select {
case <-deadlineCtx.Done():
db.Logger.Error("shutdown sync failed after timeout",
"attempts", attempt,
"duration", time.Since(startTime),
"error", lastErr)
return fmt.Errorf("shutdown sync timeout after %d attempts: %w", attempt, lastErr)
case <-db.Done:
db.Logger.Warn("shutdown sync interrupted by signal",
"attempts", attempt,
"duration", time.Since(startTime),
"error", lastErr)
return fmt.Errorf("after %d attempts: %w", attempt, ErrShutdownInterrupted)
default:
}
// Log retry with hint about second signal if interruptible
if db.Done != nil {
db.Logger.Warn("shutdown sync failed, retrying (press Ctrl+C again to skip)",
"attempts", attempt,
"error", lastErr,
"elapsed", time.Since(startTime),
"remaining", time.Until(startTime.Add(timeout)))
} else {
db.Logger.Warn("shutdown sync failed, retrying",View on GitHub (pinned to 4ed7a308f6)
Solutions
- Inspect the wrapped lastErr (%w) for the true cause (auth, DNS, throttling) and fix the storage backend issue.
- Increase the shutdown sync timeout so large uploads can complete.
- Use errors.Is for known sentinel causes and retry the shutdown later; the data is still durable locally in the WAL/LTX files.
Example fix
// before
if err := store.Close(); err != nil { log.Fatal(err) }
// after
if err := store.Close(); err != nil {
var terr *litestream.ShutdownTimeoutError // or match on prefix/wrapped cause
log.Printf("shutdown sync incomplete: %v", err) // inspect %w cause: S3 403, timeout, etc.
} Defensive patterns
Strategy: try-catch
Try / catch
if err := db.Close(); err != nil {
if strings.Contains(err.Error(), "shutdown sync timeout") {
log.Printf("final sync timed out, cause: %v", errors.Unwrap(errors.Unwrap(err)))
}
return err
} Prevention
- Increase shutdown timeout to cover large WAL uploads.
- Verify storage credentials and endpoint health before shutdown windows.
- Alert on replication errors so the final sync starts from a healthy state.
When it happens
Trigger: Replica client calls (e.g. S3 uploads) keep failing or hanging — network partition, throttling, wrong credentials — until the shutdown deadline elapses during db.Close().
Common situations: S3 endpoint unreachable or credentials expired at shutdown; very large pending LTX uploads on a slow link exceeding the timeout; storage provider returning 5xx/403 repeatedly.
Understand the failure class
Background: Request timed out: what client-side request timeouts mean across libraries (Request timed out, TIMED_OUT, APITimeoutError) — this error's family across 39 libraries.
- Timeouts: ETIMEDOUT, deadlines, and hung requests — what actually expires when a request times out.
Related errors
- after %d attempts: %w
- replica sync: %w
- fetch ltx files: %w
- cannot determine L%d max ltx file for %q: %w
- http request: %w
AI-assisted analysis of benbjohnson/litestream@4ed7a308f6 (2026-09-06).
Data as JSON: /api/errors/8aac789d55bc4a0d.
Report an issue: GitHub.