Tencent/WeKnora · error

delete mismatched provider binding: %w

Error message

delete mismatched provider binding: %w

What it means

When resolveLocked finds a binding whose Provider differs from the current client's provider, it deletes the stale binding using the compare-and-delete DeleteIfMatch so only the exact old binding is removed. This error wraps a failure of that DeleteIfMatch call (Redis error). It exists to guarantee provider replacement is atomic: if the binding changed concurrently, a separate "changed during replacement" error is returned instead.

Source

Thrown at internal/sandbox/session_lifecycle.go:172

	if err != nil {
		return nil, fmt.Errorf("check owning session: %w", err)
	}
	if !exists {
		if binding != nil {
			err = l.destroyBindingLocked(ctx, key, *binding)
		}
		return nil, errors.Join(ErrSandboxSessionDeleted, err)
	}

	if binding != nil && binding.Provider != l.client.Provider() {
		deleted, err := l.bindings.DeleteIfMatch(
			ctx,
			key,
			binding.Provider,
			binding.SandboxID,
		)
		if err != nil {
			return nil, fmt.Errorf("delete mismatched provider binding: %w", err)
		}
		if !deleted {
			return nil, errors.New("mismatched provider binding changed during replacement")
		}
		binding = nil
	}

	// A stale binding is one whose sandbox boots an image the config has since
	// replaced. Rebuild waits for a turn boundary: the first resolve of a new
	// chat turn may destroy and recreate, later resolves of that same turn
	// keep the sandbox so /workspace scratch and in-flight execs survive an
	// install that landed mid-turn. A resolve with no turn lease (no AgentQA
	// in flight) still rebuilds immediately.
	//
	// Destroying before rebuilding is not optional: the recovery pass below
	// adopts any live sandbox carrying this session's metadata, so a surviving
	// one would simply be picked up again.
	if binding != nil && binding.StaleAt != nil && l.shouldRebuildStaleBinding(ctx, key) {

View on GitHub (pinned to 988cbb0330)

Solutions

  1. Fix Redis connectivity if the wrapped cause is a store error, then retry Resolve.
  2. If switching providers intentionally, complete the migration during low traffic and retry resolves so mismatched bindings drain.
  3. Verify the binding store replication/cluster is healthy so compare-and-delete ops succeed consistently.
  4. After retries fail, inspect the binding row manually and clear the stale binding to unblock resolution.

Example fix

// before
// provider switched in config, resolve fails
handle, err := lifecycle.Resolve(ctx, key)
// after
handle, err := lifecycle.Resolve(ctx, key)
if err != nil {
    var netErr *net.OpError
    if errors.As(err, &netErr) { // Redis connectivity during mismatch cleanup
        time.Sleep(backoff)
        handle, err = lifecycle.Resolve(ctx, key)
    }
}
Defensive patterns

Strategy: retry

Validate before calling

// confirm provider switch is intentional and old bindings exist only for the old provider
old, err := store.GetBinding(ctx, key)
if err == nil && old != nil && old.Provider == currentProvider {
    // no mismatch path will run
}

Try / catch

handle, err := lifecycle.Resolve(ctx, key)
if err != nil && strings.Contains(err.Error(), "delete mismatched provider binding") {
    if isRetryable(err) {
        time.Sleep(backoff)
        handle, err = lifecycle.Resolve(ctx, key)
    }
}

Prevention

When it happens

Trigger: A session was previously bound to provider A (e.g. after a provider migration or config change) and is now resolved via provider B; the DeleteIfMatch on the old binding's (provider, sandboxID) fails due to a Redis/connection error.

Common situations: Switching sandbox providers in config while old sessions still hold bindings from the previous provider, during a Redis outage or failover; partially completed provider migrations.

Related errors


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