{"record":{"id":"52f73de99c08a9bd","repo":"redis/redis-rb","slug":"invalid-schema","errorCode":null,"errorMessage":"Invalid schema","messagePattern":"Invalid schema","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/redis/commands/modules/search/index.rb","lineNumber":50,"sourceCode":"        # Create the index on the server (runs +FT.CREATE+) and return a handle to it.\n        #\n        # @example\n        #   Redis::Commands::Search::Index.create(redis, \"idx\", schema, \"hash\", prefix: \"doc\")\n        #\n        # @param redis [Redis] the client to use\n        # @param name [String] the index name\n        # @param schema [Schema] the field schema\n        # @param storage_type [String] the indexed data type (+\"hash\"+ or +\"json\"+)\n        # @param prefix [String, nil] key prefix for documents\n        # @param stopwords [Array<String>, nil] custom stopword list\n        # @param skip_initial_scan [Boolean] do not backfill existing keys (+SKIPINITIALSCAN+)\n        # @return [Index] the created index\n        # @raise [ArgumentError] if +schema+ is not a {Schema}\n        def self.create(\n          redis, name, schema, storage_type,\n          prefix: nil, stopwords: nil, skip_initial_scan: false, **options\n        )\n          raise ArgumentError, \"Invalid schema\" unless schema.is_a?(Schema)\n\n          redis.ft_create(\n            name, schema, storage_type,\n            prefix: prefix, stopwords: stopwords,\n            skip_initial_scan: skip_initial_scan, **options\n          )\n          # The Index stores the *literal* key prefix it prepends to (and strips from) document\n          # ids. A definition (which FT.CREATE prefers over the +prefix:+ keyword) carries its\n          # prefixes verbatim, e.g. \"bicycle:\"; the +prefix:+ keyword form appends the \":\". It also\n          # records the resolved storage type (HASH/JSON) so #add writes documents the right way.\n          new(\n            redis, name, schema, resolve_storage_type(storage_type, options[:definition]),\n            prefix: key_prefix(prefix, options[:definition]), stopwords: stopwords\n          )\n        end\n\n        # The single literal key prefix the Index should manage, or nil when it can't be\n        # determined unambiguously (no prefix, or a definition with several prefixes).","sourceCodeStart":32,"sourceCodeEnd":68,"githubUrl":"https://github.com/redis/redis-rb/blob/2ba9010b91dab9e0fde1fbae3a9aae003f8bc307/lib/redis/commands/modules/search/index.rb#L32-L68","documentation":"Index.create (and Redis#create_index) require the third argument to be a Redis::Commands::Search::Schema instance: the object that renders the SCHEMA clause tokens for FT.CREATE. A bare array of field objects, a hash, or a single field raises ArgumentError before anything is sent (lib/redis/commands/modules/search/index.rb:50). Build one with Schema.new(fields) or the Schema.build block DSL.","triggerScenarios":"Index.create(redis, \"idx\", [TextField.new(\"title\")], \"hash\"); passing the SchemaDefinition DSL object instead of the Schema that Schema.build returns; passing a Hash of name => field pairs from deserialized config.","commonSituations":"Assuming the API accepts a plain field list because raw FT.CREATE does; refactoring from legacy ft_create(name, *fields) call sites; schema config stored as JSON and reconstructed as the wrong type.","solutions":["Wrap fields: Schema.new([TextField.new(\"title\"), NumericField.new(\"price\")])","Or use the block DSL: Schema.build { text_field \"title\"; numeric_field \"price\" }","Type-check deserialized schema config before calling Index.create"],"exampleFix":"// before\nIndex.create(redis, \"idx\", [TextField.new(\"title\")], \"hash\")\n// after\nIndex.create(redis, \"idx\", Schema.new([TextField.new(\"title\")]), \"hash\")","handlingStrategy":"type-guard","validationCode":"schema = Schema.new(fields) unless schema.is_a?(Redis::Commands::Search::Schema)\nIndex.create(redis, name, schema, storage)","typeGuard":"schema.is_a?(Redis::Commands::Search::Schema)","tryCatchPattern":"begin\n  Index.create(redis, name, schema, storage)\nrescue ArgumentError => e\n  raise ConfigError, \"schema must be built with Schema.new or Schema.build\"\nend","preventionTips":["Construct schemas via Schema.build so the type is always right","Type-check schema objects deserialized from config before use","Keep one factory method that always returns a Schema instance"],"tags":["redis-search","ft-create","schema","argumenterror","type-check"],"backgroundTag":"wrong-argument-type","analyzedSha":"2ba9010b91dab9e0fde1fbae3a9aae003f8bc307","analyzedAt":"2026-08-23T03:54:57.017Z","schemaVersion":2},"datasetVersion":"2026-08-23T08:06:27.607Z"}