kopia/kopia · error

Invalid access level

Error message

Invalid access level: %v

What it means

AccessLevel.MarshalJSON looks up the level in accessLevelToString; if the AccessLevel value has no registered string representation, marshaling fails with "Invalid access level". This guards against out-of-range or unregistered enum values being serialized into ACL JSON.

Solutions

  1. Use only the defined AccessLevel constants (e.g. kopiaacl.Read/Write/FullAccess-style exported levels) rather than raw integers.
  2. Validate the access level before marshaling: keep a map of valid values and check membership.
  3. Re-generate the ACL with a supported level via the kopia ACL commands/API.
  4. If data came from another version, migrate the stored ACL to current enum values.

Example fix

// before
lvl := acl.AccessLevel(7)
data, err := json.Marshal(lvl) // "Invalid access level: 7"
// after
lvl := acl.AccessLevelRead // use a defined constant
if _, ok := acl.AccessLevelToString[lvl]; !ok {
    return fmt.Errorf("unsupported access level %v", lvl)
}
data, err := json.Marshal(lvl)
Defensive patterns

Strategy: validation

Validate before calling

func validAccessLevel(l acl.AccessLevel, known map[acl.AccessLevel]string) bool {
    _, ok := known[l]
    return ok
}
// check before json.Marshal(lvl)

Type guard

func isKnownAccessLevel(l acl.AccessLevel, known map[acl.AccessLevel]string) bool {
    _, ok := known[l]
    return ok
}

Try / catch

data, err := json.Marshal(aclEntry)
if err != nil && strings.Contains(err.Error(), "Invalid access level") {
    return fmt.Errorf("access level %v not supported; use a defined constant: %w", lvl, err)
}

Prevention

When it happens

Trigger: Marshaling an AccessLevel that was constructed from an unknown integer/string — e.g. AccessLevel(99) via an unchecked conversion, a value deserialized from corrupt or foreign ACL JSON, or a zero-value AccessLevel when the enum starts at 1 and no default is registered.

Common situations: Hand-editing ACL JSON with an unsupported access level string/number; upgrading/downgrading kopia so an old ACL references a level no longer defined; programmatically building ACLs with an invalid level constant.

Understand the failure class

Background: Invalid enum value errors: "Unknown type", "Invalid scope", "must be one of" — when a string is not on the library's allowed list — this error's family across 23 libraries.

Related errors


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

Appendix: source

Thrown at internal/acl/access_level.go:49

	for k, v := range accessLevelToString {
		stringToAccessLevel[v] = k
	}
}

func (a AccessLevel) String() string {
	s, ok := accessLevelToString[a]
	if !ok {
		return strconv.Itoa(int(a))
	}

	return s
}

// MarshalJSON implements json.Marshaler.
func (a AccessLevel) MarshalJSON() ([]byte, error) {
	j, ok := accessLevelToString[a]
	if !ok {
		return nil, errors.Errorf("Invalid access level: %v", a)
	}

	//nolint:wrapcheck
	return json.Marshal(j)
}

// UnmarshalJSON implements json.Unmarshaler.
func (a *AccessLevel) UnmarshalJSON(b []byte) error {
	var s string

	if err := json.Unmarshal(b, &s); err != nil {
		return errors.Wrap(err, "error unmarshaling access level")
	}

	*a = stringToAccessLevel[s]

	return nil
}

View on GitHub (pinned to 82495e54b5)