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

  1. Re-run the upgrade command and let it run until drain completes
  2. Ensure the machine/process is not interrupted (use nohup/tmux or a long system service timeout)
  3. Disconnect idle kopia clients so draining finishes faster
  4. 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

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


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)