{"record":{"id":"521e18f176ae1a83","repo":"Tencent/WeKnora","slug":"destroy-remote-sandbox-for-session-w","errorCode":null,"errorMessage":"destroy remote sandbox for session: %w","messagePattern":"destroy remote sandbox for session: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/sandbox/session_lifecycle.go","lineNumber":139,"sourceCode":"\n// Destroy removes the bound remote sandbox and then compare-deletes its\n// binding. It is idempotent for absent and already-deleted sandboxes.\nfunc (l *remoteSessionLifecycle) Destroy(\n\tctx context.Context,\n\tkey SessionSandboxKey,\n) error {\n\tif err := key.Validate(); err != nil {\n\t\treturn err\n\t}\n\terr := l.bindings.WithLifecycleLock(ctx, key, func(lockCtx context.Context) error {\n\t\tbinding, err := l.readBinding(lockCtx, key)\n\t\tif err != nil || binding == nil {\n\t\t\treturn err\n\t\t}\n\t\treturn l.destroyBindingLocked(lockCtx, key, *binding)\n\t})\n\tif err != nil {\n\t\treturn fmt.Errorf(\"destroy remote sandbox for session: %w\", err)\n\t}\n\treturn nil\n}\n\nfunc (l *remoteSessionLifecycle) resolveLocked(\n\tctx context.Context,\n\tkey SessionSandboxKey,\n) (RemoteSandboxHandle, error) {\n\tbinding, err := l.readBinding(ctx, key)\n\tif err != nil {\n\t\treturn nil, err\n\t}\n\n\texists, err := l.sessionChecker.SessionExists(ctx, key)\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"check owning session: %w\", err)\n\t}\n\tif !exists {","sourceCodeStart":121,"sourceCodeEnd":157,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/sandbox/session_lifecycle.go#L121-L157","documentation":"Destroy wraps any failure while removing the session's bound remote sandbox under the lifecycle lock — reading the binding, or destroyBindingLocked (provider destroy plus compare-delete of the binding). The library throws it so callers know cleanup did not complete; Destroy is intended to be idempotent for already-deleted sandboxes, so this error indicates a real failure (provider error, Redis error, or lock failure), not a missing sandbox.","triggerScenarios":"remoteSessionLifecycle.Destroy is called for a session key and WithLifecycleLock fails, readBinding returns an error, or destroyBindingLocked fails (provider Destroy API error, or the binding compare-delete fails).","commonSituations":"Session teardown during shutdown while Redis is down; provider API outage leaving an orphaned sandbox still billed; lock contention with an in-flight resolve; context cancelled during cleanup.","solutions":["Retry Destroy with backoff — it is idempotent; transient provider/Redis failures resolve on re-run.","Unwrap the cause: if the provider destroy failed, check the provider console/API for the sandbox ID and delete it manually to avoid orphaned billing.","Verify Redis/binding store health if the wrapped cause is a store error.","If it is a lock timeout, retry later or investigate the node holding the lifecycle lock."],"exampleFix":"// before\nif err := lifecycle.Destroy(ctx, key); err != nil {\n    log.Printf(\"destroy failed: %v\", err)\n}\n// after\nif err := lifecycle.Destroy(ctx, key); err != nil {\n    log.Printf(\"destroy failed: %v; retrying\", err)\n    if retryErr := lifecycle.Destroy(ctxWithTimeout(30*time.Second), key); retryErr != nil {\n        log.Printf(\"destroy still failing, orphan risk: %v\", retryErr) // page/queue for manual cleanup\n    }\n}","handlingStrategy":"retry","validationCode":"if err := key.Validate(); err != nil {\n    return err\n}\nif err := rdb.Ping(ctx).Err(); err != nil {\n    return fmt.Errorf(\"redis down; defer destroy: %w\", err)\n}","typeGuard":null,"tryCatchPattern":"err := lifecycle.Destroy(ctx, key)\nif err != nil {\n    // Destroy is idempotent — retry with backoff before surfacing\n    for i := 0; i < 3 && err != nil; i++ {\n        time.Sleep(time.Duration(1<<i) * time.Second)\n        err = lifecycle.Destroy(ctx, key)\n    }\n}\nif err != nil {\n    queueOrphanCleanup(key) // provider console sweep to avoid billed orphans\n}","preventionTips":["Treat Destroy as idempotent and always retry before alerting.","Schedule a periodic orphan-sandbox sweep in the provider account.","Keep cleanupTimeout large enough for provider delete latency.","Do not cancel request contexts mid-cleanup; use a detached context with its own timeout."],"tags":["sandbox","cleanup","lifecycle"],"backgroundTag":"sandbox-destroy-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T15:18:49.778Z"}