semaphoreui/semaphore · error
cannot set value
Error message
cannot set value
What it means
setBasicType in services/project/backup_marshal.go:112 uses reflection (reflect.Value.Set) to write decoded backup JSON into a struct field. Before setting, it checks v.CanSet(); if the reflect.Value was not obtained addressably (e.g. from a non-addressable value or an unexported field), Set is impossible and the function returns "cannot set value". This protects against a reflect panic.
Solutions
- Pass a pointer to the destination struct to the backup Unmarshal function (Unmarshal(data, &target)).
- Ensure all fields with backup tags are exported (start with an uppercase letter).
- Remove the backup/db tag from unexported fields, or skip them like the marshaler does with tag "-".
- If calling setBasicType directly (tests), obtain the field via reflect.New(t).Elem() rather than reflect.ValueOf(structValue).
Example fix
// before
target := db.Project{}
Unmarshal(data, target) // fields not settable
// after
target := db.Project{}
Unmarshal(data, &target) Defensive patterns
Strategy: type-guard
Validate before calling
rv := reflect.ValueOf(target)
if rv.Kind() != reflect.Ptr || rv.IsNil() { return errors.New("target must be a non-nil pointer") }
if !rv.Elem().CanSet() { return errors.New("target not settable") } Type guard
func isSettable(v reflect.Value) bool { return v.IsValid() && v.CanSet() } Try / catch
if err := project.Unmarshal(data, &target); err != nil {
if strings.Contains(err.Error(), "cannot set value") {
// destination was not addressable; fix call site to pass a pointer
}
return err
} Prevention
- Always pass a pointer to Unmarshal.
- Keep backup-tagged fields exported.
- Never tag unexported fields with backup/db tags.
When it happens
Trigger: unmarshalValueWithBackupTags reaches a basic (non-struct/slice/map) field whose reflect.Value is not addressable - typically when unmarshaling into a non-pointer value, a copy of a struct, or an unexported struct field reached via reflect.
Common situations: Calling Unmarshal with a struct (not &struct) argument; unmarshaling into an element retrieved from a map by value; adding an unexported field with a backup/db tag to a backup entity struct.
Understand the failure class
Background: Type mismatch errors: IllegalArgumentException, TypeError and type guards across 150 open-source libraries — this error's family across 150 libraries.
Related errors
- unsupported kind
- expected array for slice, got %T
- expected object for map[string]interface
- expected object for map, got %T
- expected bool for field, got %T
AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07).
Data as JSON: /api/errors/cd60fc72f28aea95.
Report an issue: GitHub.
Appendix: source
Thrown at services/project/backup_marshal.go:112
for _, key := range v.MapKeys() {
// Assuming the key is a string
mapKey := fmt.Sprintf("%v", key.Interface())
mapValue, err := marshalValue(v.MapIndex(key))
if err != nil {
return nil, err
}
result[mapKey] = mapValue
}
return result, nil
}
// Handle other types (int, string, etc.)
return v.Interface(), nil
}
func setBasicType(data any, v reflect.Value) error {
if !v.CanSet() {
return fmt.Errorf("cannot set value")
}
switch v.Kind() {
case reflect.Bool:
b, ok := data.(bool)
if !ok {
return fmt.Errorf("expected bool for field, got %T", data)
}
v.SetBool(b)
case reflect.String:
s, ok := data.(string)
if !ok {
return fmt.Errorf("expected string for field, got %T", data)
}
v.SetString(s)
case reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:
n, ok := toFloat64(data)
if !ok {View on GitHub (pinned to 1774ccb71a)