ipfs/kubo · error

repo is closed

Error message

repo is closed

What it means

FSRepo.Close returns this when the repo has already been closed (r.closed is true). The repo tracks a closed flag guarded by packageLock, and any Close after the first is rejected. It is a lifecycle misuse signal: close once, in one place.

Source

Thrown at repo/fsrepo/fsrepo.go:544

func (r *FSRepo) readSpec() (string, error) {
	fn, err := config.Path(r.path, specFn)
	if err != nil {
		return "", err
	}
	b, err := os.ReadFile(fn)
	if err != nil {
		return "", err
	}
	return strings.TrimSpace(string(b)), nil
}

// Close closes the FSRepo, releasing held resources.
func (r *FSRepo) Close() error {
	packageLock.Lock()
	defer packageLock.Unlock()

	if r.closed {
		return errors.New("repo is closed")
	}

	err := os.Remove(filepath.Join(r.path, apiFile))
	if err != nil && !os.IsNotExist(err) {
		log.Warn("error removing api file: ", err)
	}

	err = os.Remove(filepath.Join(r.path, gatewayFile))
	if err != nil && !os.IsNotExist(err) {
		log.Warn("error removing gateway file: ", err)
	}

	if err := r.ds.Close(); err != nil {
		return err
	}

	// This code existed in the previous versions, but
	// EventlogComponent.Close was never called. Preserving here

View on GitHub (pinned to 329838acdf)

Solutions

  1. Track whether your code already closed the repo before calling Close again
  2. Use sync.Once around the Close call
  3. Route all shutdown through a single owner function so Close is called exactly once

Example fix

// before
repo.Close()
defer repo.Close()
// after
var closeOnce sync.Once
close := func() { closeOnce.Do(func() { repo.Close() }) }
close()
defer close()
Defensive patterns

Strategy: try-catch

Validate before calling

// track state yourself
if repoAlreadyClosed { return nil }

Try / catch

if err := repo.Close(); err != nil {
    if err.Error() == "repo is closed" { return nil } // already closed
    return err
}

Prevention

When it happens

Trigger: Calling Close() twice on the same *FSRepo, e.g. explicit Close followed by a deferred Close in a cleanup path, or a shutdown handler racing a deferred close.

Common situations: Daemon shutdown code and tests that defer fsrepo.Close() while also calling it explicitly; wrapping a repo in a struct with its own Close that delegates to FSRepo.Close and is invoked by two owners.

Related errors


AI-assisted analysis of ipfs/kubo@329838acdf (2026-09-03). Data as JSON: /api/errors/9ba4904dd7e09cff. Report an issue: GitHub.