kopia/kopia · error

ACL entry for a given user and target already exists

Error message

ACL entry for a given user and target already exists %v: %v

What it means

AddACL found an existing entry with the same user and target, and the requested entry's access level is lower than the existing one while overwrite=false. To avoid silently downgrading permissions, the operation is refused. Set overwrite=true to replace the entry regardless.

Solutions

  1. Pass overwrite=true to AddACL (or the appropriate CLI flag) to replace the existing entry
  2. Remove the existing ACL entry first, then add the new one
  3. Choose an equal or higher access level if the downgrade was unintentional

Example fix

// before
AddACL(ctx, rep, entry, false)
// after
AddACL(ctx, rep, entry, true)
Defensive patterns

Strategy: validation

Validate before calling

entries, _ := acl.LoadEntries(ctx, rep, nil)
for _, e := range entries {
    if e.User == newUser && maps.Equal(e.Target, newTarget) && newEntry.Access < e.Access {
        return fmt.Errorf("would downgrade %s; pass overwrite=true intentionally", newUser)
    }
}

Prevention

When it happens

Trigger: Calling AddACL without overwrite for a (User, Target) pair that already exists with a higher access level, e.g. adding a 'read' ACL for a user who already has 'write' on the same target.

Common situations: Re-running an ACL provisioning script after permissions were raised; assuming AddACL upserts by default; conflicting ACLs created by two administrators.

Understand the failure class

Background: "already exists" / EEXIST / FileAlreadyExistsException: what the 'file already exists' error means and how to fix it — this error's family across 37 libraries.

Related errors


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

Appendix: source

Thrown at internal/acl/acl_manager.go:126

	return result, nil
}

// AddACL validates and adds the specified ACL entry to the repository.
func AddACL(ctx context.Context, w repo.RepositoryWriter, e *Entry, overwrite bool) error {
	if err := e.Validate(); err != nil {
		return errors.Wrap(err, "error validating ACL")
	}

	entries, err := LoadEntries(ctx, w, nil)
	if err != nil {
		return errors.Wrap(err, "unable to load ACL entries")
	}

	for _, oldE := range entries {
		if e.User == oldE.User && maps.Equal(e.Target, oldE.Target) {
			if !overwrite && e.Access < oldE.Access {
				return errors.Errorf("ACL entry for a given user and target already exists %v: %v", oldE.User, oldE.Target)
			}

			if err = w.DeleteManifest(ctx, oldE.ManifestID); err != nil {
				return errors.Wrap(err, "error deleting old")
			}
		}
	}

	manifestID, err := w.PutManifest(ctx, map[string]string{
		manifest.TypeLabelKey: aclManifestType,
	}, e)
	if err != nil {
		return errors.Wrap(err, "error writing manifest")
	}

	e.ManifestID = manifestID

	return nil

View on GitHub (pinned to 82495e54b5)