larksuite/cli · error
%s must be a string
Error message
%s must be a string
What it means
A string-shape field received a value whose Go type is not string; the validator rejects it with this message before applying length/enum checks. Thrown by validateJSONValueAgainstShape in the typedStringShape branch.
Source
Thrown at shortcuts/common/typed_binder.go:416
}
return nil
case typedConstShape:
expectedJSON, err := json.Marshal(constraint.Value)
if err != nil {
return fmt.Errorf("%s has invalid const: %w", path, err)
}
expected, err := decodeJSONValidationValue(expectedJSON)
if err != nil {
return fmt.Errorf("%s has invalid const: %w", path, err)
}
if !reflect.DeepEqual(value, expected) {
return fmt.Errorf("%s must equal %v", path, constraint.Value)
}
return nil
case typedStringShape:
text, ok := value.(string)
if !ok {
return fmt.Errorf("%s must be a string", path)
}
length := len([]rune(text))
if constraint.MinLength != nil && length < *constraint.MinLength {
return fmt.Errorf("%s must contain at least %d characters", path, *constraint.MinLength)
}
if constraint.MaxLength != nil && length > *constraint.MaxLength {
return fmt.Errorf("%s must contain at most %d characters", path, *constraint.MaxLength)
}
if len(constraint.Enum) > 0 && !slices.Contains(constraint.Enum, text) {
return fmt.Errorf("%s must be one of: %s", path, strings.Join(constraint.Enum, ", "))
}
return nil
case typedBooleanShape:
boolean, ok := value.(bool)
if !ok {
return fmt.Errorf("%s must be a boolean", path)
}
if len(constraint.Enum) > 0 && !slices.Contains(constraint.Enum, boolean) {View on GitHub (pinned to 7fd6ef3c07)
Solutions
- Convert the value to string before binding (strconv.Itoa, strconv.FormatBool, fmt.Sprintf).
- Keep IDs and enums as strings end-to-end instead of letting JSON decoding widen them.
- If the field genuinely accepts non-strings, correct the shape constraint to match.
Example fix
// before
params.Set("user_id", 12345) // string field
// after
params.Set("user_id", strconv.Itoa(12345)) Defensive patterns
Strategy: type-guard
Validate before calling
if _, ok := v.(string); !ok { return fmt.Errorf("field requires a string") } Type guard
func asString(v any) (string, bool) { s, ok := v.(string); return s, ok } Try / catch
if err := binder.Set("user_id", raw); err != nil {
if strings.Contains(err.Error(), "must be a string") {
return fmt.Errorf("user_id must be a string, got %T", raw)
}
return err
} Prevention
- Keep identifiers as strings end-to-end (IDs, tokens, keys).
- Convert at the boundary: strconv.Itoa / FormatFloat / FormatBool.
- Watch for float64 from generic JSON decoding when fields are strings.
When it happens
Trigger: Passing an int, float64, bool, or other non-string type to a field whose compiled shape is typedStringShape — e.g. numeric IDs sent as numbers where the schema requires strings.
Common situations: JSON numbers decoded as float64 then passed straight through; IDs from other systems typed as int; bool flags passed where a string is declared.
Related errors
- %T is not assignable to %s
- %s does not match any allowed shape
- %s must be null
- %s has invalid const: %w
- %s must equal %v
AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04).
Data as JSON: /api/errors/e4272f35259e0fa8.
Report an issue: GitHub.