kopia/kopia · error

unable to open persistent cache

Error message

unable to open persistent cache

What it means

The persistent content cache could not be opened: cache.NewPersistentCache failed after key derivation and protection setup succeeded. Causes include an unwritable or missing cache storage directory, a corrupt existing cache (bad index/manifest files), or storage backend errors. Without the cache the API-server repository cannot open.

Solutions

  1. Check the wrapped cause; if cache corruption, delete the cache directory (safe — it is rebuilt)
  2. Ensure the cache directory path is writable by the process user and the disk is not full
  3. Verify KOPIA_CACHE_DIRECTORY / caching options point to a valid local path
  4. Check that ContentCacheSizeLimitBytes >= ContentCacheSizeBytes and other sweep settings are sane

Example fix

// before
cachingOptions.CacheDirectory = "/mnt/ro/cache"
// after
cachingOptions.CacheDirectory = filepath.Join(os.TempDir(), "kopia-cache")
Defensive patterns

Strategy: try-catch

Validate before calling

if st, err := os.Stat(cacheDir); err != nil || !st.IsDir() {
    os.MkdirAll(cacheDir, 0o700)
}
// check writability
test := filepath.Join(cacheDir, ".write-test")
if err := os.WriteFile(test, nil, 0o600); err != nil {
    return fmt.Errorf("cache dir %v not writable: %w", cacheDir, err)
}
os.Remove(test)

Try / catch

pc, err := cache.NewPersistentCache(...)
if err != nil {
    log.Printf("cache open failed (%v); retrying with clean cache dir", err)
    os.RemoveAll(cacheDir)
    pc, err = cache.NewPersistentCache(...) // retry with fresh cache
}

Prevention

When it happens

Trigger: getContentCacheOrNil calls cache.NewPersistentCache with a LocalStorage provider pointing at cachingOptions.CacheDirectory; fails when the directory cannot be created/opened, permissions deny access, or cached metadata is corrupt. Also fires when sweep settings (ContentCacheSizeLimitBytes) are invalid.

Common situations: Read-only $HOME or KOPIA_CACHE_DIRECTORY on a full disk; cache directory deleted mid-run leaving partial files; Docker container with a read-only volume mounted at the cache path; negative/zero size-limit options.

Understand the failure class

Background: "open() failed", "failed to open file", "cannot create file" — what a file open error means and how to fix it — this error's family across 42 libraries.

Related errors


AI-assisted analysis of kopia/kopia@82495e54b5 (2026-09-07). Data as JSON: /api/errors/45aac9e035cf7a2e. Report an issue: GitHub.

Appendix: source

Thrown at repo/open.go:175

	}

	cacheEncryptionKey, err := crypto.DeriveKeyFromPassword(password, saltWithPurpose, cacheEncryptionKeySize, keyAlgo)
	if err != nil {
		return nil, errors.Wrap(err, "unable to derive cache encryption key from password")
	}

	prot, err := cacheprot.AuthenticatedEncryptionProtection(cacheEncryptionKey)
	if err != nil {
		return nil, errors.Wrap(err, "unable to initialize protection")
	}

	pc, err := cache.NewPersistentCache(ctx, "cache-storage", cs, prot, cache.SweepSettings{
		MaxSizeBytes: opt.ContentCacheSizeBytes,
		LimitBytes:   opt.ContentCacheSizeLimitBytes,
		MinSweepAge:  opt.MinContentSweepAge.DurationOrDefault(content.DefaultDataCacheSweepAge),
	}, mr, timeNow)
	if err != nil {
		return nil, errors.Wrap(err, "unable to open persistent cache")
	}

	return pc, nil
}

// openAPIServer connects remote repository over Kopia API.
func openAPIServer(ctx context.Context, si *APIServerInfo, cliOpts ClientOptions, cachingOptions *content.CachingOptions, password string, options *Options) (Repository, error) {
	cachingOptions = cachingOptions.CloneOrDefault()

	mr := metrics.NewRegistry()

	contentCache, err := getContentCacheOrNil(ctx, si, cachingOptions, password, mr, options.TimeNowFunc)
	if err != nil {
		return nil, errors.Wrap(err, "error opening content cache")
	}

	closer := newRefCountedCloser(
		func(ctx context.Context) error {

View on GitHub (pinned to 82495e54b5)