redis/redis-rb · error · ArgumentError

nx and xx are mutually exclusive

Error message

nx and xx are mutually exclusive

What it means

Raised by redis-rb before anything reaches the server. JSON.SET supports two mutually exclusive conditional-write modes: nx (set only when the path does not exist) and xx (set only when it does). Passing both at once is contradictory, so json_set raises ArgumentError immediately at lib/redis/commands/modules/json.rb:95 instead of sending an invalid command. This mirrors the plain SET command, where NX and XX together are also a syntax error.

Source

Thrown at lib/redis/commands/modules/json.rb:95

      # @param [String] path a JSONPath, e.g. "$" for the document root
      # @param [Object] value a JSON-serializable Ruby object, or a pre-encoded JSON string
      #   when +raw+ is true
      # @param [Boolean] nx only set when the path does not already exist
      # @param [Boolean] xx only set when the path already exists
      # @param [Boolean] raw treat +value+ as an already-encoded JSON string and send it as-is
      # @param [String, Symbol] fpha store a numeric array as a Floating-Point
      #   Homogeneous Array of the given precision — one of +:bf16+, +:fp16+,
      #   +:fp32+, +:fp64+ (case-insensitive; Redis 8.8+). All values of the array
      #   are forced into the fixed floating-point type: +:bf16+/+:fp16+ halve
      #   memory at reduced precision, while +:fp32+/+:fp64+ keep higher precision.
      # @return [Boolean, String] when +nx+ or +xx+ is given, +true+ on success and +false+
      #   when the condition was not met; otherwise the raw +"OK"+ reply
      # @raise [ArgumentError] if both +nx+ and +xx+ are given (they are mutually exclusive),
      #   or if +fpha+ is not one of the supported types
      # @raise [Redis::CommandError] if a value in the array does not fit the chosen
      #   +fpha+ type ("value out of range for ...")
      def json_set(key, path, value, nx: false, xx: false, raw: false, fpha: nil)
        raise ArgumentError, "nx and xx are mutually exclusive" if nx && xx

        if fpha
          fpha = fpha.to_s.upcase
          unless JSON_SET_FPHA_TYPES.include?(fpha)
            raise ArgumentError, "fpha accepts only: #{JSON_SET_FPHA_TYPES.join(', ')}"
          end
        end

        value = ::JSON.generate(value) unless raw
        args = [:"JSON.SET", key, path, value]
        args << "NX" if nx
        args << "XX" if xx
        args << "FPHA" << fpha if fpha

        if nx || xx
          send_command(args, &BoolifySet)
        else
          send_command(args)

View on GitHub (pinned to 2ba9010b91)

Solutions

  1. Pass exactly one of nx: true or xx: true, or neither for an unconditional set
  2. When flags come from user input, resolve them to a single mode symbol first (nil, :nx, or :xx) and branch on it
  3. Add a guard in your wrapper that raises a clear config error if both flags are set

Example fix

// before
redis.json_set("doc", "$.a", 1, nx: true, xx: true)
// after
redis.json_set("doc", "$.a", 1, xx: true) # or nx: true, never both
Defensive patterns

Strategy: validation

Validate before calling

def json_write(redis, key, path, value, mode: nil)
  unless [nil, :nx, :xx].include?(mode)
    raise ArgumentError, "mode must be nil, :nx or :xx"
  end
  redis.json_set(key, path, value, **(mode ? { mode => true } : {}))
end

Type guard

mode.nil? || %i[nx xx].include?(mode)

Try / catch

begin
  redis.json_set(k, p, v, **flags)
rescue ArgumentError => e
  raise ConfigError, "json_set flags invalid: #{e.message}"
end

Prevention

When it happens

Trigger: redis.json_set(key, path, value, nx: true, xx: true); building flags from request params where both end up truthy, e.g. json_set(k, p, v, nx: params[:create], xx: params[:update]) with both present.

Common situations: Upsert helpers copied from code that used SET nx/xx; option hashes merged from defaults plus user overrides; porting from clients where the later flag silently wins, so authors expect the same behavior here.

Related errors


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