kopia/kopia · error
repository will remain locked until index differences are…
Error message
repository will remain locked until index differences are resolved
What it means
During `kopia repository upgrade`, validateAction logs all mismatch messages produced by CheckIndexInfo and wraps the underlying validation error with 'repository will remain locked until index differences are resolved'. The upgrade validation phase detected index differences (e.g. undeleted index blobs or pending epoch work) that make it unsafe to proceed, so the command aborts while the repository upgrade lock stays in place.
Solutions
- Wait for the required index-blob deletion delay (or run `kopia index optimize` / restart with all clients connected) so old index blobs get deleted, then retry the upgrade validate step.
- Check the logged mismatch messages above the error (CheckIndexInfo diagnostics) and address each listed index difference.
- Ensure all kopia clients are upgraded/connected and drained so no new index blobs appear during validation.
- As a last resort, revoke the lock with `kopia repository upgrade force-revert` (--force) and restart the upgrade after cleanup.
Example fix
// before kopia repository upgrade --io-drain-timeout=5s // after: wait for index blobs to become deletable and re-run validation kopia index optimize kopia repository upgrade --io-drain-timeout=30m --advance-notice=24h
Defensive patterns
Strategy: validation
Validate before calling
// before upgrading, ensure index differences are resolved
const mismatches = await runKopia(['repository', 'upgrade', 'validate', '--dry-run']);
if (mismatches.exitCode !== 0) {
throw new Error(`Index differences remain: ${mismatches.stderr}. Run 'kopia index optimize' and retry.`);
} Try / catch
try {
await upgradeRepository();
} catch (e) {
if (/index differences are resolved/.test(e.message)) {
await runKopia(['index', 'optimize']);
await retry(upgradeRepository, { retries: 1 });
}
} Prevention
- Run `kopia repository upgrade validate` first and resolve every reported mismatch.
- Ensure all clients are connected and current before starting the upgrade.
- Allow the index-blob deletion delay to elapse before upgrading to epoch format.
When it happens
Trigger: Running `kopia repository upgrade` (or `repository upgrade validate`) when CheckIndexInfo finds differences between index infos: direct-index blobs that should have been deleted, or mutable-index contents not yet migrated/compacted. validateAction returns errors.Wrap(err, ...) at cli/command_repository_upgrade.go:196.
Common situations: Upgrading to the epoch manager while some connected clients still wrote recently (index blobs not yet aged out); running the upgrade too soon after the io-drain window without all clients having flushed; interrupted previous upgrades leaving index blobs behind.
Understand the failure class
Background: "Invalid state transition" errors: "status must be X, actually Y", "already rejected/charging/uninstalled", "cannot ... while running" — what they mean when a library rejects your call — this error's family across 31 libraries.
Related errors
- epoch manager
- error listing index blobs
- failed to rollback the upgrade
- recovering indexes
- unable to determine current epoch
AI-assisted analysis of kopia/kopia@82495e54b5 (2026-09-07).
Data as JSON: /api/errors/d88c8bce1d7a28f3.
Report an issue: GitHub.
Appendix: source
Thrown at cli/command_repository_upgrade.go:196
msgs = append(msgs, fmt.Sprintf("lop-sided index entries for contentID %q at blob %q", contentID, iep1.PackBlobID))
}
// no msgs means the check passed without finding anything wrong
if len(msgs) == 0 {
log(ctx).Info("index validation succeeded")
return nil
}
// otherwise there's a problem somewhere ... log the problems
log(ctx).Error("inconsistencies found in migrated index:")
for _, m := range msgs {
log(ctx).Error(m)
}
// and return an error that states something's wrong.
return errors.Wrap(err, "repository will remain locked until index differences are resolved")
}
// CheckIndexInfo compare two index infos. If a mismatch exists, return an error with diagnostic information.
func CheckIndexInfo(i0, i1 content.Info) []string {
var q []string
switch {
case i0.FormatVersion != i1.FormatVersion:
q = append(q, fmt.Sprintf("mismatched FormatVersions: %v %v", i0.FormatVersion, i1.FormatVersion))
case i0.OriginalLength != i1.OriginalLength:
q = append(q, fmt.Sprintf("mismatched OriginalLengths: %v %v", i0.OriginalLength, i1.OriginalLength))
case i0.PackBlobID != i1.PackBlobID:
q = append(q, fmt.Sprintf("mismatched PackBlobIDs: %v %v", i0.PackBlobID, i1.PackBlobID))
case i0.PackedLength != i1.PackedLength:
q = append(q, fmt.Sprintf("mismatched PackedLengths: %v %v", i0.PackedLength, i1.PackedLength))
case i0.PackOffset != i1.PackOffset:
q = append(q, fmt.Sprintf("mismatched PackOffsets: %v %v", i0.PackOffset, i1.PackOffset))
case i0.EncryptionKeyID != i1.EncryptionKeyID:View on GitHub (pinned to 82495e54b5)