kopia/kopia · warning
upgrade drain interrupted
Error message
upgrade drain interrupted
What it means
drainAllClients waits for all connected clients to disconnect while a repository upgrade is in progress. It polls the lock status on a timer (sleepWithContext); if the context is cancelled or the sleep is interrupted before draining completes, it throws 'upgrade drain interrupted'.
Solutions
- Re-run the upgrade command and let it run until drain completes
- Ensure the machine/process is not interrupted (use nohup/tmux or a long system service timeout)
- Disconnect idle kopia clients so draining finishes faster
- Check that the upgrade lock is still valid; if it was revoked, the sibling error 'upgrade lock got revoked' will surface instead
Example fix
// before
ctx, cancel := context.WithCancel(context.Background())
defer cancel()
// interrupted by Ctrl-C
// after - shield from cancellation
runCtx := context.WithoutCancel(ctx) // or a detached context for the drain loop
if !c.sleepWithContext(runCtx, l.StatusPollInterval) { ... } Defensive patterns
Strategy: retry
Validate before calling
// before starting: ensure the context has a generous deadline ctx, cancel := context.WithTimeout(context.Background(), maxDrainTime)
Try / catch
if err := runUpgrade(ctx); errors.Is(err, context.Canceled) || strings.Contains(err.Error(), "drain interrupted") {
// resume or re-run the upgrade; the intent/lock remains
} Prevention
- Run upgrades under tmux/nohup or a service with a long timeout
- Disconnect idle clients before starting an upgrade so drain is quick
- Avoid Ctrl-C during the drain phase; poll status instead
When it happens
Trigger: During a repository upgrade, pressing Ctrl-C, the process receiving SIGINT/SIGTERM, or the parent context being cancelled while the loop is sleeping between status polls (l.StatusPollInterval).
Common situations: Operator aborts a long-running upgrade because drain is slow; systemd or CI kills the upgrade command on timeout; running the upgrade in a shell session that disconnects.
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.
Related errors
- error finalizing upgrade
- error getting blob configuration
- error getting repository features
- error setting parameters
- error upgrading indices
AI-assisted analysis of kopia/kopia@82495e54b5 (2026-09-07).
Data as JSON: /api/errors/d61b0a5208dd5876.
Report an issue: GitHub.
Appendix: source
Thrown at cli/command_repository_upgrade.go:429
upgradeTime := l.UpgradeTime()
now := rep.Time()
log(ctx).Infof("Waiting for %s to allow all other kopia clients to drain ...", upgradeTime.Sub(rep.Time()).Round(time.Second))
locked, writersDrained := l.IsLocked(now)
if locked {
if writersDrained {
// we have the lock now
break
}
} else {
return errors.Wrap(err, "upgrade lock got revoked after the intent was placed, giving up")
}
// TODO: this can get stuck
if !c.sleepWithContext(ctx, l.StatusPollInterval) {
return errors.New("upgrade drain interrupted")
}
}
return nil
}
// upgrade phase performs the actual upgrade action that upgrades the target
// repository. This phase runs after the lock has been acquired in one of the
// prior phases.
func (c *commandRepositoryUpgrade) upgrade(ctx context.Context, rep repo.DirectRepositoryWriter) error {
mp, mperr := rep.ContentReader().ContentFormat().GetMutableParameters(ctx)
if mperr != nil {
return errors.Wrap(mperr, "mutable parameters")
}
rf, err := rep.FormatManager().RequiredFeatures(ctx)
if err != nil {
return errors.Wrap(err, "error getting repository features")View on GitHub (pinned to 82495e54b5)