{"record":{"id":"a69bed0ea54b8076","repo":"Tencent/WeKnora","slug":"resolve-remote-sandbox-for-session-w","errorCode":null,"errorMessage":"resolve remote sandbox for session: %w","messagePattern":"resolve remote sandbox for session: %w","errorType":"exception","errorClass":null,"httpStatus":null,"severity":"error","filePath":"internal/sandbox/session_lifecycle.go","lineNumber":114,"sourceCode":"func (l *remoteSessionLifecycle) Resolve(\n\tctx context.Context,\n\tkey SessionSandboxKey,\n) (RemoteSandboxHandle, error) {\n\tif err := key.Validate(); err != nil {\n\t\treturn nil, err\n\t}\n\n\tvar handle RemoteSandboxHandle\n\terr := l.bindings.WithLifecycleLock(ctx, key, func(lockCtx context.Context) error {\n\t\tresolved, err := l.resolveLocked(lockCtx, key)\n\t\tif err != nil {\n\t\t\treturn err\n\t\t}\n\t\thandle = resolved\n\t\treturn nil\n\t})\n\tif err != nil {\n\t\treturn nil, fmt.Errorf(\"resolve remote sandbox for session: %w\", err)\n\t}\n\tif handle == nil {\n\t\treturn nil, errors.New(\"resolve remote sandbox returned no handle\")\n\t}\n\treturn handle, nil\n}\n\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)","sourceCodeStart":96,"sourceCodeEnd":132,"githubUrl":"https://github.com/Tencent/WeKnora/blob/988cbb03305e055d8ebb7d46d9ac6cc0803cd074/internal/sandbox/session_lifecycle.go#L96-L132","documentation":"Resolve wraps any failure inside the per-session lifecycle lock — binding reads, session existence checks, provider-mismatch cleanup, stale rebuilds, connect, recovery, or create — with this error. The sandbox for the session could not be resolved, so no handle is returned. Callers (including several tests) rely on the wrapped cause to distinguish deleted sessions (ErrSandboxSessionDeleted), transient provider errors, and lock timeouts.","triggerScenarios":"remoteSessionLifecycle.Resolve is called for a session key and the inner resolveLocked fails: WithLifecycleLock times out or errors, the owning session is gone, the provider create/connect call fails, or a binding compare-delete races and loses.","commonSituations":"Chat turn starts while another node holds the lifecycle lock; the owning chat session was deleted concurrently (returns ErrSandboxSessionDeleted wrapped here); the remote provider API is down or rate-limiting; Redis binding-store errors during compare-delete.","solutions":["Unwrap the chain with errors.Is/As: check for ErrSandboxSessionDeleted (do not retry — the session is gone) versus transient provider/Redis errors (retry with backoff).","Verify the session key (tenant/session IDs) is correct and the session still exists in the durable store.","Check remote provider API health/quotas if the wrapped cause is a create/connect failure.","If it is a lock timeout, reduce lock contention or inspect for leaked lifecycle locks in the binding store."],"exampleFix":"// before\nhandle, err := lifecycle.Resolve(ctx, key)\nif err != nil {\n    return err\n}\n// after\nhandle, err := lifecycle.Resolve(ctx, key)\nif err != nil {\n    if errors.Is(err, sandbox.ErrSandboxSessionDeleted) {\n        return nil // session ended; nothing to resolve\n    }\n    return fmt.Errorf(\"resolve sandbox: %w\", err) // retryable path\n}","handlingStrategy":"try-catch","validationCode":"if err := key.Validate(); err != nil {\n    return fmt.Errorf(\"invalid session key before resolve: %w\", err)\n}\nexists, err := checker.SessionExists(ctx, key)\nif err == nil && !exists {\n    return nil // skip resolve; session already gone\n}","typeGuard":"func isSessionDeletedErr(err error) bool {\n    return errors.Is(err, sandbox.ErrSandboxSessionDeleted)\n}","tryCatchPattern":"handle, err := lifecycle.Resolve(ctx, key)\nswitch {\ncase err == nil:\n    // use handle\ncase errors.Is(err, sandbox.ErrSandboxSessionDeleted):\n    return nil // expected teardown path\ndefault:\n    return fmt.Errorf(\"resolve remote sandbox for session: %w\", err) // retry/backoff\n}","preventionTips":["Always special-case ErrSandboxSessionDeleted before generic retries.","Give Resolve a context budget covering lock wait plus provider calls.","Monitor provider API error rates feeding Resolve.","Avoid resolving sandboxes for sessions you just deleted."],"tags":["sandbox","lifecycle","session"],"backgroundTag":"sandbox-resolve-failed","analyzedSha":"988cbb03305e055d8ebb7d46d9ac6cc0803cd074","analyzedAt":"2026-09-02T14:41:08.344Z","contentChangedAt":null,"schemaVersion":2},"datasetVersion":"2026-09-08T10:18:20.063Z"}