{"record":{"id":"e4272f35259e0fa8","repo":"larksuite/cli","slug":"s-must-be-a-string","errorCode":null,"errorMessage":"%s must be a string","messagePattern":"(.+?) must be a string","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"shortcuts/common/typed_binder.go","lineNumber":416,"sourceCode":"\t\t}\n\t\treturn nil\n\tcase typedConstShape:\n\t\texpectedJSON, err := json.Marshal(constraint.Value)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"%s has invalid const: %w\", path, err)\n\t\t}\n\t\texpected, err := decodeJSONValidationValue(expectedJSON)\n\t\tif err != nil {\n\t\t\treturn fmt.Errorf(\"%s has invalid const: %w\", path, err)\n\t\t}\n\t\tif !reflect.DeepEqual(value, expected) {\n\t\t\treturn fmt.Errorf(\"%s must equal %v\", path, constraint.Value)\n\t\t}\n\t\treturn nil\n\tcase typedStringShape:\n\t\ttext, ok := value.(string)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"%s must be a string\", path)\n\t\t}\n\t\tlength := len([]rune(text))\n\t\tif constraint.MinLength != nil && length < *constraint.MinLength {\n\t\t\treturn fmt.Errorf(\"%s must contain at least %d characters\", path, *constraint.MinLength)\n\t\t}\n\t\tif constraint.MaxLength != nil && length > *constraint.MaxLength {\n\t\t\treturn fmt.Errorf(\"%s must contain at most %d characters\", path, *constraint.MaxLength)\n\t\t}\n\t\tif len(constraint.Enum) > 0 && !slices.Contains(constraint.Enum, text) {\n\t\t\treturn fmt.Errorf(\"%s must be one of: %s\", path, strings.Join(constraint.Enum, \", \"))\n\t\t}\n\t\treturn nil\n\tcase typedBooleanShape:\n\t\tboolean, ok := value.(bool)\n\t\tif !ok {\n\t\t\treturn fmt.Errorf(\"%s must be a boolean\", path)\n\t\t}\n\t\tif len(constraint.Enum) > 0 && !slices.Contains(constraint.Enum, boolean) {","sourceCodeStart":398,"sourceCodeEnd":434,"githubUrl":"https://github.com/larksuite/cli/blob/7fd6ef3c07182257ce776cdc5a614e122d5bd4b3/shortcuts/common/typed_binder.go#L398-L434","documentation":"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.","triggerScenarios":"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.","commonSituations":"JSON numbers decoded as float64 then passed straight through; IDs from other systems typed as int; bool flags passed where a string is declared.","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."],"exampleFix":"// before\nparams.Set(\"user_id\", 12345) // string field\n\n// after\nparams.Set(\"user_id\", strconv.Itoa(12345))","handlingStrategy":"type-guard","validationCode":"if _, ok := v.(string); !ok { return fmt.Errorf(\"field requires a string\") }","typeGuard":"func asString(v any) (string, bool) { s, ok := v.(string); return s, ok }","tryCatchPattern":"if err := binder.Set(\"user_id\", raw); err != nil {\n    if strings.Contains(err.Error(), \"must be a string\") {\n        return fmt.Errorf(\"user_id must be a string, got %T\", raw)\n    }\n    return err\n}","preventionTips":["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."],"tags":["go","schema-validation","type-mismatch","string"],"backgroundTag":"type-mismatch","analyzedSha":"7fd6ef3c07182257ce776cdc5a614e122d5bd4b3","analyzedAt":"2026-09-04T21:17:44.649Z","contentChangedAt":"2026-09-04T21:17:44.649Z","schemaVersion":2},"datasetVersion":"2026-09-12T02:17:10.037Z"}