{"record":{"id":"5995587e61054094","repo":"semaphoreui/semaphore","slug":"cannot-assign-value-of-type-s-to-field-of-type-s","errorCode":null,"errorMessage":"cannot assign value of type %s to field of type %s","messagePattern":"cannot assign value of type (.+?) to field of type (.+?)","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"critical","filePath":"util/config.go","lineNumber":1365,"sourceCode":"\t\t\t}\n\t\t\tattribute.Set(reflect.ValueOf(arr))\n\t\tcase reflect.Map:\n\t\t\tmapType := attribute.Type()\n\t\t\tmapValue := reflect.New(mapType)\n\t\t\terr := json.Unmarshal([]byte(value), mapValue.Interface())\n\t\t\tif err != nil {\n\t\t\t\tpanic(err)\n\t\t\t}\n\t\t\tattribute.Set(mapValue.Elem())\n\t\tdefault:\n\t\t\tnewValue, _ := CastValueToKind(value, kind)\n\t\t\tconvertedValue := reflect.ValueOf(newValue)\n\t\t\tif convertedValue.Type().AssignableTo(attribute.Type()) {\n\t\t\t\tattribute.Set(convertedValue)\n\t\t\t} else if convertedValue.Type().ConvertibleTo(attribute.Type()) {\n\t\t\t\tattribute.Set(convertedValue.Convert(attribute.Type()))\n\t\t\t} else {\n\t\t\t\tpanic(fmt.Errorf(\"cannot assign value of type %s to field of type %s\", convertedValue.Type(), attribute.Type()))\n\t\t\t}\n\t\t}\n\n\t} else {\n\t\tpanic(fmt.Errorf(\"got non-existent config attribute\"))\n\t}\n}\n\nfunc getConfigValue(path string) string {\n\tattribute := reflect.ValueOf(Config)\n\tnested_path := strings.Split(path, \".\")\n\n\tfor i, nested := range nested_path {\n\t\tattribute = reflect.Indirect(attribute).FieldByName(nested)\n\t\tlastDepth := len(nested_path) == i+1\n\t\tif !lastDepth && attribute.Kind() != reflect.Struct && attribute.Kind() != reflect.Pointer ||\n\t\t\tlastDepth && attribute.Kind() == reflect.Invalid {\n\t\t\tpanic(fmt.Errorf(\"got non-existent config attribute '%v'\", path))","sourceCodeStart":1347,"sourceCodeEnd":1383,"githubUrl":"https://github.com/semaphoreui/semaphore/blob/1774ccb71a0a8b82eb74ea24c23ac9ab713de2fa/util/config.go#L1347-L1383","documentation":"setConfigValue panics when a scalar config value cannot be assigned to the target field's Go type via reflection: the cast result is neither assignable nor convertible to the field type. This happens for kinds outside String/Int/Bool that CastValueToKind does not handle (it returns the original value unchanged), or genuinely incompatible types.","triggerScenarios":"Assigning a string/env value to a config field of an unsupported kind (e.g. time.Duration, struct, custom type) so reflect cannot convert string to it; passing a bool-cast value where the field is a non-convertible type.","commonSituations":"Config fields whose types changed between versions; users putting arbitrary strings into fields typed as non-string kinds; env-var overrides applied via reflection at startup.","solutions":["Change the config value to match the field's expected type (string/int/bool as documented)","Check the field's declared type in util.ConfigType and supply a compatible literal","Cast the value before it reaches config loading (e.g. parse durations/ints yourself in pre-processing)","Update to a version where the field type matches your value format"],"exampleFix":"// before\nSLACK_NOTIFICATION_URL=12345   // field expects a URL string but cast path hits incompatible type\n// after\nSLACK_NOTIFICATION_URL=https://hooks.slack.com/services/T000/B000/XXXX","handlingStrategy":"validation","validationCode":"field, ok := reflect.TypeOf(util.ConfigType{}).FieldByName(\"SomeField\")\nif ok && field.Type.Kind() == reflect.String {\n    // value must be a plain string, not an int/bool/duration literal\n}","typeGuard":"func kindSupported(v reflect.Value) bool {\n    switch v.Kind() {\n    case reflect.String, reflect.Int, reflect.Bool:\n        return true\n    }\n    return false\n}","tryCatchPattern":"func safeSet(attr reflect.Value, val string) (err error) {\n    defer func() {\n        if r := recover(); r != nil {\n            err = fmt.Errorf(\"type mismatch setting config: %v\", r)\n        }\n    }()\n    util.SetConfigValue(attr, val)\n    return nil\n}","preventionTips":["Check each field's declared Go type in util.ConfigType before overriding via env","Supply ints and bools as parseable literals (\"1\", \"true\") only for fields of those kinds","Re-check config values after upgrading Semaphore versions","Keep config overrides in a reviewed file rather than ad-hoc shell exports"],"tags":["go","reflection","config","type-mismatch","panic"],"backgroundTag":"config-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"}