Tencent/WeKnora · error
destroy remote sandbox for session: %w
Error message
destroy remote sandbox for session: %w
What it means
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.
Source
Thrown at internal/sandbox/session_lifecycle.go:139
// Destroy removes the bound remote sandbox and then compare-deletes its
// binding. It is idempotent for absent and already-deleted sandboxes.
func (l *remoteSessionLifecycle) Destroy(
ctx context.Context,
key SessionSandboxKey,
) error {
if err := key.Validate(); err != nil {
return err
}
err := l.bindings.WithLifecycleLock(ctx, key, func(lockCtx context.Context) error {
binding, err := l.readBinding(lockCtx, key)
if err != nil || binding == nil {
return err
}
return l.destroyBindingLocked(lockCtx, key, *binding)
})
if err != nil {
return fmt.Errorf("destroy remote sandbox for session: %w", err)
}
return nil
}
func (l *remoteSessionLifecycle) resolveLocked(
ctx context.Context,
key SessionSandboxKey,
) (RemoteSandboxHandle, error) {
binding, err := l.readBinding(ctx, key)
if err != nil {
return nil, err
}
exists, err := l.sessionChecker.SessionExists(ctx, key)
if err != nil {
return nil, fmt.Errorf("check owning session: %w", err)
}
if !exists {View on GitHub (pinned to 988cbb0330)
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.
Example fix
// before
if err := lifecycle.Destroy(ctx, key); err != nil {
log.Printf("destroy failed: %v", err)
}
// after
if err := lifecycle.Destroy(ctx, key); err != nil {
log.Printf("destroy failed: %v; retrying", err)
if retryErr := lifecycle.Destroy(ctxWithTimeout(30*time.Second), key); retryErr != nil {
log.Printf("destroy still failing, orphan risk: %v", retryErr) // page/queue for manual cleanup
}
} Defensive patterns
Strategy: retry
Validate before calling
if err := key.Validate(); err != nil {
return err
}
if err := rdb.Ping(ctx).Err(); err != nil {
return fmt.Errorf("redis down; defer destroy: %w", err)
} Try / catch
err := lifecycle.Destroy(ctx, key)
if err != nil {
// Destroy is idempotent — retry with backoff before surfacing
for i := 0; i < 3 && err != nil; i++ {
time.Sleep(time.Duration(1<<i) * time.Second)
err = lifecycle.Destroy(ctx, key)
}
}
if err != nil {
queueOrphanCleanup(key) // provider console sweep to avoid billed orphans
} Prevention
- 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.
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- sandbox session no longer exists
- resolve remote sandbox for session: %w
- sandbox: no live sandbox for session %s
- sandbox: config is missing required fields
- cube remote client config is required
AI-assisted analysis of Tencent/WeKnora@988cbb0330 (2026-09-02).
Data as JSON: /api/errors/521e18f176ae1a83.
Report an issue: GitHub.