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
- Pass exactly one of nx: true or xx: true, or neither for an unconditional set
- When flags come from user input, resolve them to a single mode symbol first (nil, :nx, or :xx) and branch on it
- 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
- Model the conditional mode as one symbol (:nx or :xx) instead of two booleans
- Never spread an unfiltered params hash into json_set kwargs
- Assert at most one of nx/xx in wrappers that forward options
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
- fpha accepts only: #{JSON_SET_FPHA_TYPES.join(', ')}
- wrong number of arguments (expected key/path/value triplets)
- index requires a path
- collect fields must be :all or a non-empty list
- Invalid phonetic matcher. Supported matchers are: #{valid_ma
AI-assisted analysis of redis/redis-rb@2ba9010b91 (2026-08-23).
Data as JSON: /api/errors/fd3454836eead1af.
Report an issue: GitHub.