semaphoreui/semaphore · error
expected bool for field, got %T
Error message
expected bool for field, got %T
What it means
Inside setBasicType (services/project/backup_marshal.go:119), when the destination field's reflect.Kind is Bool, the incoming JSON data must be a Go bool (JSON true/false). If data is any other type (string, number, null, map), the function returns "expected bool for field, got %T" naming the actual Go type received. It exists to prevent reflect panics from SetBool on non-bool data.
Solutions
- Open the backup JSON, find the field, and change its value to a real JSON boolean (true/false, unquoted).
- If the field is genuinely numeric in your data, change the Go struct field type to match (int/float) instead of bool.
- Pre-validate the backup file (jq -e '... | type == "boolean"') before running the restore.
- Re-export the backup with a compatible version of the tool so types match the current struct definitions.
Example fix
// before (backup.json) "allow_pprof": "true" // after "allow_pprof": true
Defensive patterns
Strategy: validation
Validate before calling
// before restore, check field type
var raw map[string]any
json.Unmarshal(backupData, &raw)
if v, ok := raw["allow_pprof"]; ok {
if _, ok := v.(bool); !ok { return fmt.Errorf("field allow_pprof must be bool, got %T", v) }
} Type guard
func isBool(v any) bool { _, ok := v.(bool); return ok } Try / catch
if err := project.Unmarshal(data, &target); err != nil {
var te *project.TypeError // or inspect message
if strings.Contains(err.Error(), "expected bool for field") {
return fmt.Errorf("invalid backup: %w", err)
}
return err
} Prevention
- Write real JSON booleans (true/false), never "true"/1.
- Validate backups against a JSON schema before restoring.
- Keep export and import tool versions in sync.
When it happens
Trigger: unmarshalValueWithBackupTags encounters a backup-tagged bool struct field whose JSON value is not a boolean - e.g. a string "true", the number 1, or null from the backup file.
Common situations: Hand-edited backup JSON where a boolean field was written as "true" (string) or 1; backups exported from older/newer schema versions where a field changed from int to bool; JSON produced by another tool that serializes booleans as strings.
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
- expected string for field, got %T
- expected number for field, got %T
- expected object for struct, got %T
- must be valid JSON
- key can not be empty
AI-assisted analysis of semaphoreui/semaphore@1774ccb71a (2026-09-07).
Data as JSON: /api/errors/d2be117dd0c8a36e.
Report an issue: GitHub.
Appendix: source
Thrown at services/project/backup_marshal.go:119
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 {
return fmt.Errorf("expected number for field, got %T", data)
}
v.SetInt(int64(n))
case reflect.Uint, reflect.Uint8, reflect.Uint16, reflect.Uint32, reflect.Uint64:
n, ok := toFloat64(data)
if !ok {
return fmt.Errorf("expected number for field, got %T", data)View on GitHub (pinned to 1774ccb71a)