redis/redis-rb · error · ArgumentError

can't supply both nx and xx

Error message

can't supply both nx and xx

What it means

geoadd maps the nx:/xx: keywords to the server's GEOADD NX and XX flags. NX means 'only add new members, never update existing ones'; XX means 'only update existing members, never add' — mutually exclusive by definition. Passing both truthy raises ArgumentError 'can't supply both nx and xx' client-side before anything is sent (the server would reject NX XX too).

Source

Thrown at lib/redis/commands/geo.rb:15

# frozen_string_literal: true

class Redis
  module Commands
    module Geo
      # Adds the specified geospatial items (latitude, longitude, name) to the specified key
      #
      # @param [String] key
      # @param [Array] member arguemnts for member or members: longitude, latitude, name
      # @param [Boolean] nx don't update already existing elements, always add new ones (since Redis 6.2)
      # @param [Boolean] xx only update elements that already exist, never add new ones (since Redis 6.2)
      # @param [Boolean] ch modify the return value to the number of changed elements (since Redis 6.2)
      # @return [Integer] number of elements added to the sorted set, or changed when `ch` is set
      def geoadd(key, *member, nx: false, xx: false, ch: false)
        raise ArgumentError, "can't supply both nx and xx" if nx && xx

        args = [:geoadd, key]
        args << "NX" if nx
        args << "XX" if xx
        args << "CH" if ch
        args.concat(member)
        send_command(args)
      end

      # Returns geohash string representing position for specified members of the specified key.
      #
      # @param [String] key
      # @param [String, Array<String>] member one member or array of members
      # @return [Array<String, nil>] returns array containg geohash string if member is present, nil otherwise
      def geohash(key, member)
        send_command([:geohash, key, member])
      end

View on GitHub (pinned to 2ba9010b91)

Solutions

  1. Pick one semantic: nx: true (add-only) or xx: true (update-only), never both
  2. When merging option hashes, resolve conflicts explicitly: delete :nx if :xx is set (or vice versa) before the call
  3. ch: true composes with either flag — use it freely when you need changed-element counts

Example fix

# before
redis.geoadd('Sicily', 13.36, 38.11, 'Palermo', nx: true, xx: true)  # ArgumentError

# after
redis.geoadd('Sicily', 13.36, 38.11, 'Palermo', nx: true, ch: true)
Defensive patterns

Strategy: validation

Validate before calling

opts = { nx: nx, xx: xx, ch: ch }.compact
opts.delete(:nx) if opts[:xx]  # resolve the conflict explicitly: xx wins
redis.geoadd(key, *member, **opts)

Try / catch

begin
  redis.geoadd(key, *member, nx: nx, xx: xx)
rescue ArgumentError => e
  raise if e.message != "can't supply both nx and xx"
  redis.geoadd(key, *member, xx: true)  # apply one semantic
end

Prevention

When it happens

Trigger: redis.geoadd('Sicily', 13.36, 38.11, 'Palermo', nx: true, xx: true); option hashes merged from defaults plus user input where both keys end up set.

Common situations: Config-driven option merging (default nx: true, user overrides xx: true); wrappers around SET-style APIs where nx/xx pairs are familiar; enabling both flags 'to be safe'.

Related errors


AI-assisted analysis of redis/redis-rb@2ba9010b91 (2026-08-23). Data as JSON: /api/errors/30ce1b4a942f2c78. Report an issue: GitHub.