Tencent/WeKnora · error

delete sandbox binding: %w

Error message

delete sandbox binding: %w

What it means

Returned by RedisSessionSandboxBindingStore.DeleteIfMatch when the Lua script run (deleteBindingIfMatchScript) fails — the same transport/server conditions as any Redis write: unreachable server, cancelled context, script error, or NOAUTH/READONLY. The conditional delete did not happen. Wrapped as 'delete sandbox binding: %w'. Returning (false, nil) means the key was absent or did not match provider/sandbox ID — not an error.

Source

Thrown at internal/sandbox/session_binding_redis.go:174

// DeleteIfMatch atomically deletes only the expected provider and sandbox ID.
func (s *RedisSessionSandboxBindingStore) DeleteIfMatch(
	ctx context.Context,
	key SessionSandboxKey,
	provider RemoteProvider,
	sandboxID string,
) (bool, error) {
	if err := validateBindingMatch(key, provider, sandboxID); err != nil {
		return false, err
	}
	deleted, err := deleteBindingIfMatchScript.Run(
		ctx,
		s.client,
		[]string{s.bindingKey(key)},
		string(provider),
		sandboxID,
	).Int64()
	if err != nil {
		return false, fmt.Errorf("delete sandbox binding: %w", err)
	}
	return deleted != 0, nil
}

// WithLifecycleLock serializes create, recover, replace, and delete transitions
// across all WeKnora processes sharing Redis.
func (s *RedisSessionSandboxBindingStore) WithLifecycleLock(
	ctx context.Context,
	key SessionSandboxKey,
	fn func(context.Context) error,
) error {
	if err := key.Validate(); err != nil {
		return err
	}
	if fn == nil {
		return errors.New("sandbox lifecycle lock callback is required")
	}
	return redislock.WithRenewableLock(

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Retry DeleteIfMatch — it is idempotent (false, nil) when the binding is already gone
  2. redis-cli ping and check server logs for READONLY/OOM/cluster errors
  3. Verify ctx is not cancelled before the cleanup call and that timeouts allow the round-trip
  4. For cluster setups, confirm hash-tag routing keeps the binding key stable across resharding

Example fix

// before
if _, err := store.DeleteIfMatch(ctx, key, provider, sandboxID); err != nil {
    return fmt.Errorf("teardown: %w", err)
}
// after: treat transient delete failure as non-fatal, retry once
if _, err := store.DeleteIfMatch(ctx, key, provider, sandboxID); err != nil {
    time.Sleep(200 * time.Millisecond)
    if _, rerr := store.DeleteIfMatch(ctx, key, provider, sandboxID); rerr != nil {
        // binding TTL-less but harmless: resolve re-validates it
        log.Printf("deferred binding delete for %s: %v", key.SessionID, rerr)
    }
}
Defensive patterns

Strategy: retry

Validate before calling

// check the binding exists and matches before issuing a delete
cur, err := store.Get(ctx, key)
if err == nil && cur != nil && cur.Provider == provider && cur.SandboxID == sandboxID {
    // safe to call DeleteIfMatch
    _ = cur
}

Type guard

func isDeleteTransportError(err error) bool {
    return err != nil && strings.Contains(err.Error(), "delete sandbox binding")
}
// note: (false, nil) simply means no match — not an error

Try / catch

deleted, err := store.DeleteIfMatch(ctx, key, provider, sandboxID)
if isDeleteTransportError(err) {
    // idempotent: safe to retry; if it ultimately fails, the leftover binding is harmless
    log.Printf("binding delete deferred (self-heals via resolve validation): %v", err)
}

Prevention

When it happens

Trigger: Calling DeleteIfMatch(ctx, key, provider, sandboxID) when Redis is down, ctx times out while the script runs, the cluster marks the slot as migrating, or the server rejects the EVAL (OOM, read-only replica, script busy).

Common situations: Failover/sentinel blip during session teardown; context cancelled because the HTTP request was abandoned mid-cleanup; Redis Cluster resharding moved the hash tag's slot; NOSCRIPT after SCRIPT FLUSH (go-redis re-sends automatically, but older versions could surface it).

Related errors


AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02). Data as JSON: /api/errors/f9d2e14092fd132d. Report an issue: GitHub.