{"record":{"id":"c61b7833252294f2","repo":"teamcapybara/capybara","slug":"invalid-option-s-invalid-names-should-be-one-c61b78","errorCode":null,"errorMessage":"Invalid option(s) #{invalid_names}, should be one of #{valid_names}","messagePattern":"Invalid option\\(s\\) #(.+?), should be one of #(.+?)","errorType":"exception","errorClass":"ArgumentError","httpStatus":null,"severity":"error","filePath":"lib/capybara/queries/selector_query.rb","lineNumber":360,"sourceCode":"        @custom_keys ||= node_filters.keys + expression_filters.keys\n      end\n\n      def assert_valid_keys\n        unless VALID_MATCH.include?(match)\n          raise ArgumentError, \"Invalid option #{match.inspect} for :match, should be one of #{VALID_MATCH.map(&:inspect).join(', ')}\"\n        end\n\n        unhandled_options = @options.keys.reject do |option_name|\n          valid_keys.include?(option_name) ||\n            expression_filters.any? { |_name, ef| ef.handles_option? option_name } ||\n            node_filters.any? { |_name, nf| nf.handles_option? option_name }\n        end\n\n        return if unhandled_options.empty?\n\n        invalid_names = unhandled_options.map(&:inspect).join(', ')\n        valid_names = (valid_keys - [:allow_self]).map(&:inspect).join(', ')\n        raise ArgumentError, \"Invalid option(s) #{invalid_names}, should be one of #{valid_names}\"\n      end\n\n      def filtered_expression(expr)\n        conditions = {}\n        conditions[:id] = options[:id] if use_default_id_filter?\n        conditions[:class] = options[:class] if use_default_class_filter?\n        conditions[:style] = options[:style] if use_default_style_filter? && !options[:style].is_a?(Hash)\n        builder(expr).add_attribute_conditions(**conditions)\n      end\n\n      def use_default_id_filter?\n        options.key?(:id) && !custom_keys.include?(:id)\n      end\n\n      def use_default_class_filter?\n        options.key?(:class) && !custom_keys.include?(:class)\n      end\n","sourceCodeStart":342,"sourceCodeEnd":378,"githubUrl":"https://github.com/teamcapybara/capybara/blob/15b5fdb76e972e9623d5af2123ed3755594f9732/lib/capybara/queries/selector_query.rb#L342-L378","documentation":"The second check in SelectorQuery#assert_valid_keys rejects any option key the query cannot handle. Valid keys are the built-in ones (VALID_KEYS: :above, :below, :left_of, :right_of, :near, :count, :minimum, :maximum, :between, :text, :id, :class, :style, :visible, :obscured, :exact, :exact_text, :normalize_ws, :match, :wait, :filter_set, :focused) plus every node/expression filter registered on the current selector. An unlisted key is almost always a typo'd filter name or a filter that belongs to a different selector.","triggerScenarios":"find('.row', text: 'Hi', placaholder: 'Email'); find(:field, 'Email', placeholder_text: 'Email') (the filter is :placeholder); find(:link, href: 'x', disabled: true) where :disabled is not a filter of :link; splatting an options hash with extra keys, e.g. find('.x', **user_params) where user_params contains app-level keys like :timeout.","commonSituations":"Using :timeout instead of the built-in :wait; passing a filter from one selector to another (e.g. :rows on :css); Capybara upgrades that moved filters into filter_sets or renamed them; custom selectors whose filters were never registered; copy-pasting options from find_field documentation into have_selector calls.","solutions":["Match the invalid name from the message against the selector's filter list and fix the typo - e.g. :placaholder/:placeholder_text -> :placeholder.","Use the correct built-in key for the intent: :wait for timing (not :timeout), :count/:minimum/:maximum/:between for occurrence counts, :text for content.","Confirm the filter exists for the selector kind you passed it to (the :field selector accepts :placeholder, a raw :css/'...' query does not).","If you genuinely need a new option, register it with Capybara.add_selector or Selector#expression_filter/node_filter so custom_keys covers it."],"exampleFix":"# before\nfind(:field, 'Email', placaholder: 'Email')\n\n# after\nfind(:field, 'Email', placeholder: 'Email')","handlingStrategy":"validation","validationCode":"options = { text: 'Hi', placeholder: 'Email' }\nallowed = %i[text placeholder] # filters that exist for :field\nunknown = options.keys - allowed\nraise ArgumentError, \"unsupported options: #{unknown.inspect}\" unless unknown.empty?\nfind(:field, 'Email', **options)","typeGuard":"def supported_options?(selector_kind, options)\n  filters = Capybara::Selector.new(Capybara.send(:find_selector, selector_kind)).expressions.keys # introspect as needed\n  (options.keys - filters).empty?\nend","tryCatchPattern":null,"preventionTips":["Centralize query options in typed helpers so stray keys (e.g. :timeout) are caught at one place with a clear message.","Check the selector's documentation (rdoc for Capybara::Selector) for its filter list before adding new options.","After Capybara upgrades, run the suite once loudly - renamed filters surface through this error."],"tags":["ruby","capybara","selector-query","filters","option-validation"],"backgroundTag":"unknown-keyword-argument","analyzedSha":"15b5fdb76e972e9623d5af2123ed3755594f9732","analyzedAt":"2026-08-21T16:53:45.588Z","schemaVersion":2},"datasetVersion":"2026-08-21T18:17:14.833Z"}