{"id":"e4ef29845b9acc1a","repo":"go-redis/redis","slug":"ft-create-on-hash-and-on-json-are-mutually-exclus","errorCode":null,"errorMessage":"FT.CREATE: ON HASH and ON JSON are mutually exclusive","messagePattern":"FT\\.CREATE: ON HASH and ON JSON are mutually exclusive","errorType":"validation","errorClass":null,"httpStatus":null,"severity":"error","filePath":"search_commands.go","lineNumber":1395,"sourceCode":"// FTCreate - Creates a new index with the given options and schema.\n// The 'index' parameter specifies the name of the index to create.\n// The 'options' parameter specifies various options for the index, such as:\n// whether to index hashes or JSONs, prefixes, filters, default language, score, score field, payload field, etc.\n// The 'schema' parameter specifies the schema for the index, which includes the field name, field type, etc.\n// For more information, please refer to the Redis documentation:\n// [FT.CREATE]: (https://redis.io/commands/ft.create/)\nfunc (c cmdable) FTCreate(ctx context.Context, index string, options *FTCreateOptions, schema ...*FieldSchema) *StatusCmd {\n\targs := []interface{}{\"FT.CREATE\", index}\n\tif options != nil {\n\t\tif options.OnHash && !options.OnJSON {\n\t\t\targs = append(args, \"ON\", \"HASH\")\n\t\t}\n\t\tif options.OnJSON && !options.OnHash {\n\t\t\targs = append(args, \"ON\", \"JSON\")\n\t\t}\n\t\tif options.OnHash && options.OnJSON {\n\t\t\tcmd := NewStatusCmd(ctx, args...)\n\t\t\tcmd.SetErr(fmt.Errorf(\"FT.CREATE: ON HASH and ON JSON are mutually exclusive\"))\n\t\t\treturn cmd\n\t\t}\n\t\tif options.Prefix != nil {\n\t\t\targs = append(args, \"PREFIX\", len(options.Prefix))\n\t\t\targs = append(args, options.Prefix...)\n\t\t}\n\t\tif options.Filter != \"\" {\n\t\t\targs = append(args, \"FILTER\", options.Filter)\n\t\t}\n\t\tif options.DefaultLanguage != \"\" {\n\t\t\targs = append(args, \"LANGUAGE\", options.DefaultLanguage)\n\t\t}\n\t\tif options.LanguageField != \"\" {\n\t\t\targs = append(args, \"LANGUAGE_FIELD\", options.LanguageField)\n\t\t}\n\t\tif options.Score > 0 {\n\t\t\targs = append(args, \"SCORE\", options.Score)\n\t\t}","sourceCodeStart":1377,"sourceCodeEnd":1413,"githubUrl":"https://github.com/go-redis/redis/blob/36d97525cd8076aed67cddf54778e9ea84550929/search_commands.go#L1377-L1413","documentation":"Returned by FTCreate when FTCreateOptions has both OnHash and OnJSON set to true. An FT.CREATE index targets exactly one document type (HASH or JSON); specifying both is contradictory, so the builder attaches the error to the returned StatusCmd instead of sending an invalid command.","triggerScenarios":"Calling FTCreate with &FTCreateOptions{OnHash: true, OnJSON: true}. The error is set on the returned *StatusCmd and surfaces on .Err()/.Result(). Triggered before any FT.CREATE argument beyond the index name is serialized.","commonSituations":"Defaulting both flags true in a config struct. UI that exposes HASH and JSON as independent toggles and allows both selected. Reusing an options value and flipping one flag without clearing the other. Migrating between HASH and JSON indexes without resetting the prior flag.","solutions":["Set exactly one of OnHash or OnJSON (or leave both false to let the server apply its default, typically HASH).","Audit the FTCreateOptions literal at the call site to enforce mutual exclusion.","When exposing the choice to users, coerce a single enum into OnHash xor OnJSON before building options."],"exampleFix":"// before\nclient.FTCreate(ctx, \"idx\", &FTCreateOptions{OnHash: true, OnJSON: true}, schema...)\n// after\nclient.FTCreate(ctx, \"idx\", &FTCreateOptions{OnJSON: true}, schema...)","handlingStrategy":"validation","validationCode":"func validateFTCreateOn(opts *FTCreateOptions) error {\n\tif opts.OnHash && opts.OnJSON {\n\t\treturn fmt.Errorf(\"choose HASH or JSON, not both\")\n\t}\n\treturn nil\n}","typeGuard":"func onChoiceIsValid(opts *FTCreateOptions) bool {\n\treturn !(opts.OnHash && opts.OnJSON)\n}","tryCatchPattern":"cmd := client.FTCreate(ctx, \"idx\", opts, schema...)\nif err := cmd.Err(); err != nil {\n    if strings.Contains(err.Error(), \"ON HASH and ON JSON are mutually exclusive\") {\n        // clear one of OnHash/OnJSON and retry\n    }\n    return err\n}","preventionTips":["Expose the index type as a single enum (hash/json) and map it to OnHash xor OnJSON.","Review FTCreateOptions literals for both flags set, especially defaults.","Always call cmd.Err() on the returned StatusCmd so this validation surfaces."],"tags":["ft-create","validation","index-options","api-misuse"],"analyzedSha":"36d97525cd8076aed67cddf54778e9ea84550929","analyzedAt":"2026-08-06T01:08:27.376Z","schemaVersion":2}