Tencent/WeKnora · error
read sandbox turn lease: %w
Error message
read sandbox turn lease: %w
What it means
TurnState reads the per-session turn-lease hash from Redis to report whether a chat turn is open and whether its one stale-sandbox rebuild is still unspent. This error wraps any failure of the HGetAll call against the Redis key `weknora:sandbox:session:{<hash>}:turn`, so callers know the turn state could not be determined. It is thrown because acting on stale-binding decisions requires authoritative lease state; guessing would risk tearing down a sandbox mid-turn.
Source
Thrown at internal/sandbox/session_binding_redis.go:351
}
if err := endTurnScript.Run(ctx, s.client, []string{s.turnKey(key)}).Err(); err != nil {
return fmt.Errorf("end sandbox turn lease: %w", err)
}
return nil
}
// TurnState reports whether a chat turn is open and whether its first
// resolve may still rebuild a stale sandbox.
func (s *RedisSessionSandboxBindingStore) TurnState(
ctx context.Context,
key SessionSandboxKey,
) (bool, bool, error) {
if err := key.Validate(); err != nil {
return false, false, err
}
values, err := s.client.HGetAll(ctx, s.turnKey(key)).Result()
if err != nil {
return false, false, fmt.Errorf("read sandbox turn lease: %w", err)
}
if len(values) == 0 {
return false, false, nil
}
_ = s.client.PExpire(ctx, s.turnKey(key), sessionTurnLeaseTTL).Err()
refs, _ := strconv.Atoi(values["refs"])
if refs <= 0 {
return false, false, nil
}
return true, values["rebuild"] == "1", nil
}
// ConsumeTurnRebuild spends the one rebuild allowed for the current turn.
func (s *RedisSessionSandboxBindingStore) ConsumeTurnRebuild(
ctx context.Context,
key SessionSandboxKey,
) error {
if err := key.Validate(); err != nil {View on GitHub (pinned to 988cbb0330)
Solutions
- Check Redis connectivity and health (PING) from the WeKnora node and fix connection settings (address, password, TLS) in config.
- Inspect the wrapped error (errors.Unwrap / %w chain) to distinguish timeouts, auth errors, and read-only replica errors and fix accordingly.
- Retry TurnState with backoff; it is a read and safe to re-issue.
- If on a replica, ensure reads go to the primary or enable read-replica support in the Redis client.
Example fix
// before
open, canRebuild, err := store.TurnState(ctx, key)
if err != nil {
return fmt.Errorf("turn state unavailable: %v", err)
}
// after
open, canRebuild, err := store.TurnState(ctx, key)
if err != nil {
if errors.Is(err, context.DeadlineExceeded) {
ctx, cancel = context.WithTimeout(context.Background(), 2*time.Second)
defer cancel()
open, canRebuild, err = store.TurnState(ctx, key)
}
if err != nil {
return fmt.Errorf("turn state unavailable: %w", err)
}
} Defensive patterns
Strategy: retry
Validate before calling
if err := rdb.Ping(ctx).Err(); err != nil {
return fmt.Errorf("redis unavailable before TurnState: %w", err)
} Type guard
func isRedisErr(err error) bool {
var rerr *redis.RedisError
return errors.As(err, &rerr)
} Try / catch
open, rebuild, err := store.TurnState(ctx, key)
if err != nil {
if isRetryableRedisErr(err) {
open, rebuild, err = store.TurnState(ctxWithRetryTimeout, key)
}
if err != nil { return fmt.Errorf("turn state: %w", err) }
} Prevention
- Health-check Redis before turn processing and degrade gracefully if down.
- Set generous Redis timeouts and pool limits for turn-lease operations.
- Monitor Redis latency/errors and alert before they surface as turn-lease failures.
- Use the same Redis cluster/config across all WeKnora nodes.
When it happens
Trigger: RedisSessionSandboxBindingStore.TurnState is called while Redis is unreachable, the connection is closed/timed out, the key was migrated or is on a read-only replica, auth fails, or the context is cancelled mid-HGetAll.
Common situations: Redis outage or failover during a chat turn; wrong REDIS address/password in config; Redis under memory pressure evicting connections; network partition between the WeKnora node and Redis cluster; context deadline exceeded under load.
Related errors
- consume sandbox turn rebuild: %w
- WEKNORA_REDIS_NAMESPACE must not contain braces
- WEKNORA_REDIS_NAMESPACE must not contain control characters
- delete terminal sandbox binding: %w
- sandbox: config is missing required fields
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/954efab9bad3a0b2.
Report an issue: GitHub.