Troubleshooting
Redis connection errors
If you see TrypemaError::RedisError with connection-related messages:
- Check Redis is running and accessible:
redis-cli ping
# Should respond: PONG
- Check Redis version (must be >= 7.2):
redis-cli INFO server | grep redis_version
- Check your connection URL -- make sure it matches what you passed to
redis::Client::open(). - Check network accessibility -- if Redis is on a different host, ensure the port is open and there are no firewall rules blocking access.
RedisKey validation errors
If RedisKey::try_from(...) returns Err(TrypemaError::InvalidRedisKey(...)):
- The key must be non-empty.
- The key must be <= 255 bytes.
- The key must not contain
:(colon is used internally as a separator).
use trypema::redis::RedisKey;
// These will fail:
// RedisKey::try_from("".to_string()) // empty
// RedisKey::try_from("user:123".to_string()) // contains ':'
// This works:
let key = RedisKey::try_from("user_123".to_string()).unwrap();
Configuration validation errors
Many configuration types are validated newtypes. Common failures:
| Type | Validation | Error |
|---|---|---|
RateLimit::try_from(value) | Must be > 0.0 | InvalidRateLimit |
WindowSizeSeconds::try_from(value) | Must be >= 1 | InvalidWindowSizeSeconds |
RateGroupSizeMs::try_from(value) | Must be >= 1 | InvalidRateGroupSizeMs |
HardLimitFactor::try_from(value) | Must be >= 1.0 | InvalidHardLimitFactor |
SuppressionFactorCacheMs::try_from(value) | Must be >= 1 | InvalidSuppressionFactorCacheMs |
SyncIntervalMs::try_from(value) | Must be >= 1 | InvalidRateGroupSizeMs |
All of these return TrypemaError variants and can be handled with ? or match.
Cleanup loop does not seem to run
With redis-tokio
Redis cleanup is spawned on the current Tokio runtime via Handle::try_current(). If there is no active Tokio runtime when you call run_cleanup_loop(), Redis cleanup will not start (a warning is logged).
Fix: Call run_cleanup_loop() from within an async context where a Tokio runtime is active.
With redis-smol
The cleanup task is a detached Smol task. It only makes progress if your application drives a Smol executor (e.g., smol::block_on(...)).
Without Redis features
Local cleanup uses a background std::thread. It should always work unless the thread fails to spawn.
Rate limits not being enforced as expected
Sticky rate limits
Remember that rate limits are sticky: the first inc() call for a key stores the rate limit. Subsequent calls for the same key ignore the rate_limit argument. If you are passing different rate limits for the same key, only the first one takes effect.
Fix: Use a consistent rate limit for each key. If you need to change a key's limit, let the old entry expire or be cleaned up.
Best-effort concurrency
Under high concurrency, multiple threads can observe "allowed" simultaneously and all proceed, causing temporary overshoot. This is by design for performance. If you need strict serialisation, use external synchronization (e.g., per-key mutexes).
Suppressed strategy: Rejected never appears
The suppressed strategy never returns Rejected. Over the hard limit, it returns Suppressed { is_allowed: false, suppression_factor: 1.0 }. If you are matching on Rejected in a suppressed context, you will never hit that arm.
Redis keys look wrong in Redis CLI
Trypema uses a structured key schema. Each user key expands into multiple Redis keys:
{prefix}:{user_key}:{rate_type}:{suffix}
For example, user_123 with the absolute strategy and default prefix creates:
trypema:user_123:absolute:h
trypema:user_123:absolute:a
trypema:user_123:absolute:w
trypema:user_123:absolute:t
This is expected. See Redis Provider for the full data model.
Hybrid provider: decisions lag behind Redis
The hybrid provider serves decisions from local state and syncs to Redis every sync_interval_ms. This means decisions may be based on stale state for up to sync_interval_ms.
Fix: Reduce sync_interval_ms for fresher state (at the cost of more Redis writes). Or use the pure Redis provider if real-time accuracy is critical.
Multi-instance configuration mismatch
If different instances use different values for window_size_seconds, rate_group_size_ms, hard_limit_factor, or suppression_factor_cache_ms, rate limiting behaviour will be inconsistent. The first instance to create a key stores the window limit in Redis; other instances will use that stored value, but their local calculations may differ.
Fix: Keep all configuration options identical across all instances.
Error types reference
| Error | Cause |
|---|---|
TrypemaError::RedisError(...) | Redis connectivity or command failure. |
TrypemaError::InvalidRateLimit(...) | Rate limit value <= 0. |
TrypemaError::InvalidWindowSizeSeconds(...) | Window size < 1. |
TrypemaError::InvalidRateGroupSizeMs(...) | Rate group size == 0. |
TrypemaError::InvalidHardLimitFactor(...) | Hard limit factor < 1.0. |
TrypemaError::InvalidRedisKey(...) | Redis key empty, > 255 bytes, or contains :. |
TrypemaError::InvalidSuppressionFactorCacheMs(...) | Suppression factor cache == 0. |
TrypemaError::UnexpectedRedisScriptResult(...) | Lua script returned unexpected result (likely a bug -- please report). |
TrypemaError::CustomError(...) | Internal error. |
Next steps
- Configuration & Tuning -- verify your configuration
- Cleanup Loop -- ensure cleanup is running

