{"record":{"id":"f9d2e14092fd132d","repo":"Tencent/WeKnora","slug":"delete-sandbox-binding-w","errorCode":null,"errorMessage":"delete sandbox binding: %w","messagePattern":"delete sandbox binding: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/sandbox/session_binding_redis.go","lineNumber":174,"sourceCode":"// DeleteIfMatch atomically deletes only the expected provider and sandbox ID.\nfunc (s *RedisSessionSandboxBindingStore) DeleteIfMatch(\n\tctx context.Context,\n\tkey SessionSandboxKey,\n\tprovider RemoteProvider,\n\tsandboxID string,\n) (bool, error) {\n\tif err := validateBindingMatch(key, provider, sandboxID); err != nil {\n\t\treturn false, err\n\t}\n\tdeleted, err := deleteBindingIfMatchScript.Run(\n\t\tctx,\n\t\ts.client,\n\t\t[]string{s.bindingKey(key)},\n\t\tstring(provider),\n\t\tsandboxID,\n\t).Int64()\n\tif err != nil {\n\t\treturn false, fmt.Errorf(\"delete sandbox binding: %w\", err)\n\t}\n\treturn deleted != 0, nil\n}\n\n// WithLifecycleLock serializes create, recover, replace, and delete transitions\n// across all WeKnora processes sharing Redis.\nfunc (s *RedisSessionSandboxBindingStore) WithLifecycleLock(\n\tctx context.Context,\n\tkey SessionSandboxKey,\n\tfn func(context.Context) error,\n) error {\n\tif err := key.Validate(); err != nil {\n\t\treturn err\n\t}\n\tif fn == nil {\n\t\treturn errors.New(\"sandbox lifecycle lock callback is required\")\n\t}\n\treturn redislock.WithRenewableLock(","sourceCodeStart":156,"sourceCodeEnd":192,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/sandbox/session_binding_redis.go#L156-L192","documentation":"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.","triggerScenarios":"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).","commonSituations":"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).","solutions":["Retry DeleteIfMatch — it is idempotent (false, nil) when the binding is already gone","redis-cli ping and check server logs for READONLY/OOM/cluster errors","Verify ctx is not cancelled before the cleanup call and that timeouts allow the round-trip","For cluster setups, confirm hash-tag routing keeps the binding key stable across resharding"],"exampleFix":"// before\nif _, err := store.DeleteIfMatch(ctx, key, provider, sandboxID); err != nil {\n    return fmt.Errorf(\"teardown: %w\", err)\n}\n// after: treat transient delete failure as non-fatal, retry once\nif _, err := store.DeleteIfMatch(ctx, key, provider, sandboxID); err != nil {\n    time.Sleep(200 * time.Millisecond)\n    if _, rerr := store.DeleteIfMatch(ctx, key, provider, sandboxID); rerr != nil {\n        // binding TTL-less but harmless: resolve re-validates it\n        log.Printf(\"deferred binding delete for %s: %v\", key.SessionID, rerr)\n    }\n}","handlingStrategy":"retry","validationCode":"// check the binding exists and matches before issuing a delete\ncur, err := store.Get(ctx, key)\nif err == nil && cur != nil && cur.Provider == provider && cur.SandboxID == sandboxID {\n    // safe to call DeleteIfMatch\n    _ = cur\n}","typeGuard":"func isDeleteTransportError(err error) bool {\n    return err != nil && strings.Contains(err.Error(), \"delete sandbox binding\")\n}\n// note: (false, nil) simply means no match — not an error","tryCatchPattern":"deleted, err := store.DeleteIfMatch(ctx, key, provider, sandboxID)\nif isDeleteTransportError(err) {\n    // idempotent: safe to retry; if it ultimately fails, the leftover binding is harmless\n    log.Printf(\"binding delete deferred (self-heals via resolve validation): %v\", err)\n}","preventionTips":["Always call DeleteIfMatch, never raw DEL, so concurrent rebinds are not clobbered","Wrap session teardown in a context with its own timeout independent of the request","Treat delete errors as non-fatal in cleanup paths (resolve re-validates stale bindings)","Alert on repeated delete failures — they usually coincide with broader Redis incidents"],"tags":["redis","lua-script","network","go"],"backgroundTag":"redis-unreachable","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}