{"record":{"id":"59aad66da3d9fd10","repo":"microsoft/FASTER","slug":"beginlockable-cannot-be-called-twice-call-endlockable-first","errorCode":null,"errorMessage":"BeginLockable cannot be called twice (call EndLockable first)","messagePattern":"BeginLockable cannot be called twice \\(call EndLockable first\\)","errorType":"exception","errorClass":"FasterException","httpStatus":null,"severity":"error","filePath":"cs/src/core/ClientSession/ClientSession.cs","lineNumber":103,"sourceCode":"        }\n\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        private void InternalReleaseLockable()\n        {\n            isAcquiredLockable = false;\n            fht.DecrementNumLockingSessions();\n        }\n\n        internal void CheckIsAcquiredLockable()\n        {\n            if (!isAcquiredLockable)\n                throw new FasterException(\"Lockable method call when BeginLockable has not been called\");\n        }\n\n        void CheckIsNotAcquiredLockable()\n        {\n            if (isAcquiredLockable)\n                throw new FasterException(\"BeginLockable cannot be called twice (call EndLockable first)\");\n        }\n\n        internal ClientSession(\n            FasterKV<Key, Value> fht,\n            FasterKV<Key, Value>.FasterExecutionContext<Input, Output, Context> ctx,\n            Functions functions,\n            SessionVariableLengthStructSettings<Value, Input> sessionVariableLengthStructSettings,\n            ILoggerFactory loggerFactory = null)\n        {\n            this.bContext = new(this);\n            this.uContext = new(this);\n\n            if (fht.LockTable.IsEnabled)\n            {\n                this.lContext = new(this);\n                this.luContext = new(this);\n            }\n","sourceCodeStart":85,"sourceCodeEnd":121,"githubUrl":"https://github.com/microsoft/FASTER/blob/321d872eabda6a0345c8bd76419f89723ed864ae/cs/src/core/ClientSession/ClientSession.cs#L85-L121","documentation":"CheckIsNotAcquiredLockable throws this FasterException when AcquireLockable (BeginLockable) is called on a session that is already in the lockable-acquired state. A session supports only one outstanding BeginLockable; the caller must EndLockable first before beginning again.","triggerScenarios":"Calling AcquireLockable twice without an intervening ReleaseLockable/EndLockable - e.g. re-entering a locking region on the same ClientSession while isAcquiredLockable is still true.","commonSituations":"Nested or re-entrant locking logic on the same session; concurrent threads sharing one ClientSession and both calling AcquireLockable; retry loops that re-acquire without releasing after a failed operation.","solutions":["Ensure each AcquireLockable is paired with a ReleaseLockable before the next AcquireLockable.","Do not share a single ClientSession across threads that each attempt BeginLockable; give each thread its own session.","Guard acquisition with a flag or check so re-entrant calls are skipped instead of re-acquired.","Fix retry logic to release the lockable state (EndLockable) before re-attempting AcquireLockable."],"exampleFix":"// before\nsession.AcquireLockable();\nsession.AcquireLockable(); // throws - already acquired\n// after\nif (!sessionAcquired)\n{\n    session.AcquireLockable();\n    sessionAcquired = true;\n}","handlingStrategy":"validation","validationCode":"if (sessionLockableAcquired) throw new InvalidOperationException(\"EndLockable must be called before AcquireLockable again\");","typeGuard":null,"tryCatchPattern":"try\n{\n    session.AcquireLockable();\n}\ncatch (FasterException)\n{\n    // already acquired; release first or use a separate session\n}","preventionTips":["Never share a ClientSession across threads performing locking","Pair every AcquireLockable with a ReleaseLockable before re-acquiring","In retry loops, always EndLockable before re-beginning"],"tags":["locking","lifecycle","concurrency","csharp"],"backgroundTag":"invalid-state-transition","analyzedSha":"321d872eabda6a0345c8bd76419f89723ed864ae","analyzedAt":"2026-09-15T22:18:00.693Z","contentChangedAt":"2026-09-15T22:18:00.693Z","schemaVersion":2},"datasetVersion":"2026-09-16T09:17:16.951Z"}