semaphoreui/semaphore · error
expected number for field, got %T
Error message
expected number for field, got %T
What it means
In setBasicType (services/project/backup_marshal.go:131), for signed integer fields (Int..Int64), the data is run through toFloat64; if the value is not a JSON number (or numeric type), the function returns "expected number for field, got %T". toFloat64 only accepts float/int/uint variants, so strings, bools, nil, and objects all fail.
Solutions
- Edit the backup JSON so the field is an unquoted JSON number ("42" -> 42).
- If the source data is a string of digits, convert it in the exporter or change the struct field to string.
- Handle null explicitly: replace null with a numeric default (e.g. 0) in the backup, or make the field a pointer (*int) so nil data is handled upstream.
- Re-export the backup with a matching tool version.
Example fix
// before (backup.json) "build_number": "42" // after "build_number": 42
Defensive patterns
Strategy: validation
Validate before calling
if v, ok := raw["build_number"]; ok {
if _, ok := v.(float64); !ok { return fmt.Errorf("field build_number must be a number, got %T", v) }
} Type guard
func isJSONNumber(v any) bool { switch v.(type) { case float64, float32, int, int64: return true }; return false } Try / catch
if err := project.Unmarshal(data, &target); err != nil {
if strings.Contains(err.Error(), "expected number for field") {
return fmt.Errorf("numeric field has wrong type in backup: %w", err)
}
return err
} Prevention
- Never quote numbers in backup JSON.
- Replace null with numeric defaults, or use pointer fields for optional ints.
- Validate the backup file with a schema checker before restoring.
When it happens
Trigger: unmarshalValueWithBackupTags encounters an int-kind struct field whose backup JSON value is a string (e.g. "42"), boolean, null, or nested object.
Common situations: Backups where numeric fields were serialized as strings by a producer tool or hand-editing; null values where an int was expected; schema changes turning a string field into int between versions.
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 bool for field, got %T
- expected string 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/36857450227ef2f0.
Report an issue: GitHub.
Appendix: source
Thrown at services/project/backup_marshal.go:131
}
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)
}
v.SetUint(uint64(n))
case reflect.Float32, reflect.Float64:
n, ok := toFloat64(data)
if !ok {
return fmt.Errorf("expected number for field, got %T", data)
}
v.SetFloat(n)
default:
return fmt.Errorf("unsupported kind %v", v.Kind())
}
return nilView on GitHub (pinned to 1774ccb71a)