{"record":{"id":"a6fc1f7dd1ee9956","repo":"microsoft/FASTER","slug":"the-list-is-empty","errorCode":null,"errorMessage":"The list is empty!","messagePattern":"The list is empty!","errorType":"exception","errorClass":"InvalidOperationException","httpStatus":null,"severity":"error","filePath":"cs/remote/src/FASTER.common/ElasticCircularBuffer.cs","lineNumber":59,"sourceCode":"\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        public void Enqueue(ref T value)\n        {\n            int next = (tail + 1) & DefaultCapacity;\n            if (next == head)\n            {\n                throw new InvalidOperationException(\"The inner list is full!\");\n            }\n            Items[tail] = value;\n            tail = next;\n        }\n\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        public T Dequeue()\n        {\n            if (head == tail)\n            {\n                throw new InvalidOperationException(\"The list is empty!\");\n            }\n            int oldhead = head;\n            head = (head + 1) & DefaultCapacity;\n            var ret = Items[oldhead];\n            Items[oldhead] = default;\n            return ret;\n        }\n\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        public bool IsFull() => (((tail + 1) & DefaultCapacity) == head);\n\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        public bool IsEmpty() => (head == tail);\n\n        public IEnumerable<T> Iterate()\n        {\n            int i = head;\n            while (i != tail)","sourceCodeStart":41,"sourceCodeEnd":77,"githubUrl":"https://github.com/microsoft/FASTER/blob/321d872eabda6a0345c8bd76419f89723ed864ae/cs/remote/src/FASTER.common/ElasticCircularBuffer.cs#L41-L77","documentation":"Dequeue on the single-buffer ElasticCircularBuffer found head == tail, which in this ring design means the buffer is empty. Since there is nothing to return, the library throws InvalidOperationException rather than returning a default value, making empty-state misuse explicit.","triggerScenarios":"Calling Dequeue when no items remain — e.g., a consumer thread draining faster than producers, or checking a count/emptiness condition incorrectly (or not at all) before dequeuing.","commonSituations":"Race between consumer threads both observing items then double-dequeuing; loops that call Dequeue until an exception instead of checking IsEmpty; shutdown ordering where consumers keep polling after producers stopped.","solutions":["Check IsEmpty (or head/tail state) before calling Dequeue.","Use TryDequeue-style logic or synchronize consumers with a lock/semaphore so only one thread dequeues per available item.","In shutdown paths, signal consumers to stop polling before the buffer is drained empty."],"exampleFix":"// before\nvar item = queue.Dequeue();\n\n// after\nif (!queue.IsEmpty())\n{\n    var item = queue.Dequeue();\n}","handlingStrategy":"validation","validationCode":"if (queue.IsEmpty()) return default; // or wait for signal\nvar item = queue.Dequeue();","typeGuard":null,"tryCatchPattern":"try { item = queue.Dequeue(); }\ncatch (InvalidOperationException ex) when (ex.Message.Contains(\"empty\"))\n{\n    item = default; // queue drained; wait for producers\n}","preventionTips":["Always gate Dequeue behind IsEmpty checks.","Use a SemaphoreSlim/condition signal to wake consumers only when items exist.","Avoid loop-until-exception patterns for draining.","Synchronize multiple consumer threads to prevent double-dequeue races."],"tags":["csharp","buffer","empty","faster"],"backgroundTag":"empty-result-set","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"}