go-redis/redis · error
FT.CREATE: ON HASH and ON JSON are mutually exclusive
Error message
FT.CREATE: ON HASH and ON JSON are mutually exclusive
What it means
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.
Source
Thrown at search_commands.go:1395
// FTCreate - Creates a new index with the given options and schema.
// The 'index' parameter specifies the name of the index to create.
// The 'options' parameter specifies various options for the index, such as:
// whether to index hashes or JSONs, prefixes, filters, default language, score, score field, payload field, etc.
// The 'schema' parameter specifies the schema for the index, which includes the field name, field type, etc.
// For more information, please refer to the Redis documentation:
// [FT.CREATE]: (https://redis.io/commands/ft.create/)
func (c cmdable) FTCreate(ctx context.Context, index string, options *FTCreateOptions, schema ...*FieldSchema) *StatusCmd {
args := []interface{}{"FT.CREATE", index}
if options != nil {
if options.OnHash && !options.OnJSON {
args = append(args, "ON", "HASH")
}
if options.OnJSON && !options.OnHash {
args = append(args, "ON", "JSON")
}
if options.OnHash && options.OnJSON {
cmd := NewStatusCmd(ctx, args...)
cmd.SetErr(fmt.Errorf("FT.CREATE: ON HASH and ON JSON are mutually exclusive"))
return cmd
}
if options.Prefix != nil {
args = append(args, "PREFIX", len(options.Prefix))
args = append(args, options.Prefix...)
}
if options.Filter != "" {
args = append(args, "FILTER", options.Filter)
}
if options.DefaultLanguage != "" {
args = append(args, "LANGUAGE", options.DefaultLanguage)
}
if options.LanguageField != "" {
args = append(args, "LANGUAGE_FIELD", options.LanguageField)
}
if options.Score > 0 {
args = append(args, "SCORE", options.Score)
}View on GitHub (pinned to 36d97525cd)
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.
Example fix
// before
client.FTCreate(ctx, "idx", &FTCreateOptions{OnHash: true, OnJSON: true}, schema...)
// after
client.FTCreate(ctx, "idx", &FTCreateOptions{OnJSON: true}, schema...) Defensive patterns
Strategy: validation
Validate before calling
func validateFTCreateOn(opts *FTCreateOptions) error {
if opts.OnHash && opts.OnJSON {
return fmt.Errorf("choose HASH or JSON, not both")
}
return nil
} Type guard
func onChoiceIsValid(opts *FTCreateOptions) bool {
return !(opts.OnHash && opts.OnJSON)
} Try / catch
cmd := client.FTCreate(ctx, "idx", opts, schema...)
if err := cmd.Err(); err != nil {
if strings.Contains(err.Error(), "ON HASH and ON JSON are mutually exclusive") {
// clear one of OnHash/OnJSON and retry
}
return err
} Prevention
- 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.
When it happens
Trigger: 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.
Common situations: 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.
Related errors
- redis: please enter the command to be executed
- too many arguments
- redis: GROUPBY is not allowed when multiple aggregators are
- redis: EXCLUDEEMPTY is not allowed with GROUPBY
- redis: Watch requires at least one key
AI-assisted analysis of go-redis/redis@36d97525cd (2026-08-06).
Data as JSON: /data/errors/e4ef29845b9acc1a.json.
Report an issue: GitHub.