semaphoreui/semaphore · critical

got non-existent config attribute

Error message

got non-existent config attribute '%v'

What it means

getConfigValue panics when a dotted attribute path cannot be walked on the util.Config struct: an intermediate segment is not a struct/pointer, or the final segment does not exist (Invalid kind). It is the read-side counterpart of error 202 and indicates the path string does not match the config struct layout.

Solutions

  1. Fix the dotted path to match the actual ConfigType field hierarchy
  2. Verify each intermediate segment is a struct in util.ConfigType
  3. Validate user-supplied config paths before passing them in
  4. Pin/reference documentation for the deployed Semaphore version, since field names change

Example fix

// before
val := util.GetConfig("db.conection.host")
// after
val := util.GetConfig("db.connection.host")
Defensive patterns

Strategy: validation

Validate before calling

func configPathExists(path string) bool {
    v := reflect.Indirect(reflect.ValueOf(util.Config{}))
    parts := strings.Split(path, ".")
    for i, seg := range parts {
        if v.Kind() != reflect.Struct { return false }
        v = v.FieldByName(seg)
        if !v.IsValid() { return false }
        if i < len(parts)-1 && v.Kind() != reflect.Struct && v.Kind() != reflect.Pointer { return false }
    }
    return true
}

Type guard

func isReadableConfigValue(path string) (ok bool) {
    defer func() { _ = recover() }()
    util.GetConfig(path)
    return true
}

Try / catch

func safeGet(path string) (val string, err error) {
    defer func() {
        if r := recover(); r != nil {
            err = fmt.Errorf("bad config path %q: %v", path, r)
        }
    }()
    return util.GetConfig(path), nil
}

Prevention

When it happens

Trigger: Calling getConfigValue/GetConfig (or a feature that reads config by path) with a mistyped path like `auth.email_verification_requiredd`, or a path whose intermediate node is a scalar (e.g. `db.user.foo`).

Common situations: Typo in config key inside code/templates; fields renamed between versions; building paths programmatically from user input.

Related errors


AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07). Data as JSON: /api/errors/70f3af3fe9b7eda9. Report an issue: GitHub.

Appendix: source

Thrown at util/config.go:1383

				panic(fmt.Errorf("cannot assign value of type %s to field of type %s", convertedValue.Type(), attribute.Type()))
			}
		}

	} else {
		panic(fmt.Errorf("got non-existent config attribute"))
	}
}

func getConfigValue(path string) string {
	attribute := reflect.ValueOf(Config)
	nested_path := strings.Split(path, ".")

	for i, nested := range nested_path {
		attribute = reflect.Indirect(attribute).FieldByName(nested)
		lastDepth := len(nested_path) == i+1
		if !lastDepth && attribute.Kind() != reflect.Struct && attribute.Kind() != reflect.Pointer ||
			lastDepth && attribute.Kind() == reflect.Invalid {
			panic(fmt.Errorf("got non-existent config attribute '%v'", path))
		}
	}

	return fmt.Sprintf("%v", attribute)
}

func validate(value any) error {
	t := reflect.TypeOf(value)
	v := reflect.ValueOf(value)

	if t.Kind() == reflect.Ptr {
		t = t.Elem()
		v = reflect.Indirect(v)
	}

	for i := 0; i < t.NumField(); i++ {
		fieldType := t.Field(i)
		fieldValue := v.Field(i)

View on GitHub (pinned to 1774ccb71a)