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

  1. Convert the value to string before binding (strconv.Itoa, strconv.FormatBool, fmt.Sprintf).
  2. Keep IDs and enums as strings end-to-end instead of letting JSON decoding widen them.
  3. 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

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


AI-assisted analysis of larksuite/cli@7fd6ef3c07 (2026-09-04). Data as JSON: /api/errors/e4272f35259e0fa8. Report an issue: GitHub.