{"record":{"id":"cd60fc72f28aea95","repo":"semaphoreui/semaphore","slug":"cannot-set-value","errorCode":null,"errorMessage":"cannot set value","messagePattern":"cannot set value","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"services/project/backup_marshal.go","lineNumber":112,"sourceCode":"\t\tfor _, key := range v.MapKeys() {\n\t\t\t// Assuming the key is a string\n\t\t\tmapKey := fmt.Sprintf(\"%v\", key.Interface())\n\t\t\tmapValue, err := marshalValue(v.MapIndex(key))\n\t\t\tif err != nil {\n\t\t\t\treturn nil, err\n\t\t\t}\n\t\t\tresult[mapKey] = mapValue\n\t\t}\n\t\treturn result, nil\n\t}\n\n\t// Handle other types (int, string, etc.)\n\treturn v.Interface(), nil\n}\n\nfunc setBasicType(data any, v reflect.Value) error {\n\tif !v.CanSet() {\n\t\treturn fmt.Errorf(\"cannot set value\")\n\t}\n\n\tswitch v.Kind() {\n\tcase reflect.Bool:\n\t\tb, ok := data.(bool)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"expected bool for field, got %T\", data)\n\t\t}\n\t\tv.SetBool(b)\n\tcase reflect.String:\n\t\ts, ok := data.(string)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"expected string for field, got %T\", data)\n\t\t}\n\t\tv.SetString(s)\n\tcase reflect.Int, reflect.Int8, reflect.Int16, reflect.Int32, reflect.Int64:\n\t\tn, ok := toFloat64(data)\n\t\tif !ok {","sourceCodeStart":94,"sourceCodeEnd":130,"githubUrl":"https://github.com/semaphoreui/semaphore/blob/1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa/services/project/backup_marshal.go#L94-L130","documentation":"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.","triggerScenarios":"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.","commonSituations":"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.","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)."],"exampleFix":"// before\ntarget := db.Project{}\nUnmarshal(data, target) // fields not settable\n// after\ntarget := db.Project{}\nUnmarshal(data, &target)","handlingStrategy":"type-guard","validationCode":"rv := reflect.ValueOf(target)\nif rv.Kind() != reflect.Ptr || rv.IsNil() { return errors.New(\"target must be a non-nil pointer\") }\nif !rv.Elem().CanSet() { return errors.New(\"target not settable\") }","typeGuard":"func isSettable(v reflect.Value) bool { return v.IsValid() && v.CanSet() }","tryCatchPattern":"if err := project.Unmarshal(data, &target); err != nil {\n    if strings.Contains(err.Error(), \"cannot set value\") {\n        // destination was not addressable; fix call site to pass a pointer\n    }\n    return err\n}","preventionTips":["Always pass a pointer to Unmarshal.","Keep backup-tagged fields exported.","Never tag unexported fields with backup/db tags."],"tags":["go","reflection","unmarshal","unaddressable-value"],"backgroundTag":"type-mismatch","analyzedSha":"1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa","analyzedAt":"2026-09-07T11:00:33.293Z","contentChangedAt":"2026-09-07T11:00:33.293Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}