What just happened?
You ran an INCR, INCRBY, DECR, or DECRBY command and Redis replied with:
(error) ERR value is not an integer or out of range
Redis counter commands only work when the stored value looks like a plain decimal integer โ no floats, no JSON blobs, no empty strings. The moment something else ends up under that key, the command stops cold.
Reproduce the error in 30 seconds
# Store a float
127.0.0.1:6379> SET page_views 3.5
OK
127.0.0.1:6379> INCR page_views
(error) ERR value is not an integer or out of range
# Store a string
127.0.0.1:6379> SET page_views "hello"
OK
127.0.0.1:6379> INCR page_views
(error) ERR value is not an integer or out of range
# Store an empty string
127.0.0.1:6379> SET page_views ""
OK
127.0.0.1:6379> INCR page_views
(error) ERR value is not an integer or out of range
# Integer too large for 64-bit signed range
127.0.0.1:6379> SET big_num 9999999999999999999999
OK
127.0.0.1:6379> INCR big_num
(error) ERR value is not an integer or out of range
Diagnose your key first
Don't touch anything yet. Start by checking exactly what's stored:
127.0.0.1:6379> TYPE your_key
string
127.0.0.1:6379> GET your_key
"3.5"
127.0.0.1:6379> STRLEN your_key
(integer) 3
These are the usual suspects:
- A float like
"1.0"or"0.5"โ application code serialized a number without truncating the decimal - A JSON blob like
"{\"count\": 1}"โ someone stored the whole object instead of extracting the integer field - An empty string
""โ the key was initialized without a value - A number outside the signed 64-bit range (
-9223372036854775808to9223372036854775807) - A stale key left over from an old data model, now reused for a counter
Quick fix: reset the key to a valid integer
Need the counter working immediately? Overwrite it:
# Check current value
127.0.0.1:6379> GET page_views
"3.5"
# Overwrite with the closest integer
127.0.0.1:6379> SET page_views 3
OK
# Now INCR works
127.0.0.1:6379> INCR page_views
(integer) 4
Starting from scratch? Just delete the key and call INCR directly. Redis initializes missing keys to 0 before incrementing:
127.0.0.1:6379> DEL page_views
(integer) 1
127.0.0.1:6379> INCR page_views
(integer) 1
Fix the root cause in application code
Case 1: Your code writes a float instead of an integer
Floats are the most frequent offender. Python, JavaScript, and several other languages can serialize a number like 1 as "1.0" without any warning โ and Redis refuses to increment it.
# Python โ wrong
import redis
r = redis.Redis()
r.set('page_views', 1.0) # Stores "1.0", not "1"
r.incr('page_views') # Boom
# Python โ correct
r.set('page_views', int(1.0)) # Stores "1"
r.incr('page_views') # Works
// Node.js (ioredis) โ wrong
await redis.set('page_views', parseFloat('3.5')); // Stores "3.5"
// Node.js โ correct
await redis.set('page_views', Math.floor(3.5)); // Stores "3"
await redis.incr('page_views'); // Works
Case 2: You're storing a JSON object and trying to increment a field inside it
Redis strings don't support partial updates. Store the counter as a separate hash field and use HINCRBY instead:
# Store each field separately as a hash
127.0.0.1:6379> HSET user:42 page_views 10 login_count 3
(integer) 2
# Increment one field atomically
127.0.0.1:6379> HINCRBY user:42 page_views 1
(integer) 11
# Decrement
127.0.0.1:6379> HINCRBY user:42 login_count -1
(integer) 2
Case 3: Float counters โ use INCRBYFLOAT instead
Tracking scores, rates, or any value that needs decimal precision? Use INCRBYFLOAT. The stored value must still be numeric, but decimals are fine:
127.0.0.1:6379> SET score 10.5
OK
127.0.0.1:6379> INCRBYFLOAT score 0.5
"11"
127.0.0.1:6379> INCRBYFLOAT score 1.25
"12.25"
One gotcha: after enough INCRBYFLOAT calls, the stored value will contain a decimal point. Running plain INCR on that key later will fail. Keep float keys and integer keys separate.
Case 4: Race condition with key initialization
Two processes initializing the same key at the same time is a classic race. One writes a non-integer value; the other immediately calls INCR and hits the error. The fix is simple โ let INCR handle initialization itself:
# Don't do this (race condition)
IF key not exists:
SET counter 0
INCR counter
# Do this instead โ INCR is atomic and initializes from 0 if key is missing
INCR counter
Verify the fix
# Check the value is a valid integer string
127.0.0.1:6379> GET page_views
"4"
# INCR should return the next integer
127.0.0.1:6379> INCR page_views
(integer) 5
# DECR should work too
127.0.0.1:6379> DECR page_views
(integer) 4
# INCRBY and DECRBY with step
127.0.0.1:6379> INCRBY page_views 10
(integer) 14
127.0.0.1:6379> DECRBY page_views 3
(integer) 11
All four work without error? The key is clean.
Add a guard in code before incrementing
Production counters deserve a safety net. This Python helper validates the stored value first, resets it if corrupted, and logs a warning so you know it happened:
# Python helper
def safe_incr(r, key):
val = r.get(key)
if val is not None:
try:
int(val) # Will raise if not a valid integer string
except (ValueError, TypeError):
# Reset to 0 and log a warning
r.set(key, 0)
print(f"WARNING: reset corrupt counter key '{key}' (was: {val})")
return r.incr(key)
Quick reference: which commands are affected
INCR keyโ increment by 1INCRBY key amountโ increment by amount (integer)DECR keyโ decrement by 1DECRBY key amountโ decrement by amount (integer)
All four require the value to be a string representation of a signed 64-bit integer. INCRBYFLOAT is the one exception โ it accepts decimals, but mixing it with the integer commands on the same key will eventually land you back here.

