{"record":{"id":"cf555035b11ebcdf","repo":"microsoft/garnet","slug":"consistent-read-context-does-not-allow-writes-cf5550","errorCode":null,"errorMessage":"Consistent read context does not allow writes!","messagePattern":"Consistent read context does not allow writes!","errorType":"exception","errorClass":"TsavoriteException","httpStatus":null,"severity":"error","filePath":"libs/storage/Tsavorite/cs/src/core/ClientSession/TransactionalConsistentReadContext.cs","lineNumber":250,"sourceCode":"        /// <inheritdoc/>\n        public async ValueTask CompletePendingAsync(bool waitForCommit = false, CancellationToken token = default)\n        {\n            await TransactionalContext.CompletePendingAsync(waitForCommit, token).ConfigureAwait(false);\n            Session.functions.PostSingleKeyConsistentReadCallback();\n        }\n\n        /// <inheritdoc/>\n        public async ValueTask<CompletedOutputIterator<TInput, TOutput, TContext>> CompletePendingWithOutputsAsync(bool waitForCommit = false, CancellationToken token = default)\n        {\n            var status = await TransactionalContext.CompletePendingWithOutputsAsync(waitForCommit, token).ConfigureAwait(false);\n            Session.functions.PostSingleKeyConsistentReadCallback();\n            return status;\n        }\n\n        /// <inheritdoc/>\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        public Status Upsert(TKey key, ReadOnlySpan<byte> desiredValue, TContext userContext = default)\n            => throw new TsavoriteException(\"Consistent read context does not allow writes!\");\n\n        /// <inheritdoc/>\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        public Status Upsert(TKey key, ReadOnlySpan<byte> desiredValue, ref UpsertOptions upsertOptions, TContext userContext = default)\n            => throw new TsavoriteException(\"Transactional consistent read context does not allow writes!\");\n\n        /// <inheritdoc/>\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        public Status Upsert(TKey key, ref TInput input, ReadOnlySpan<byte> desiredValue, ref TOutput output, TContext userContext = default)\n            => throw new TsavoriteException(\"Transactional consistent read context does not allow writes!\");\n\n        /// <inheritdoc/>\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]\n        public Status Upsert(TKey key, ref TInput input, ReadOnlySpan<byte> desiredValue, ref TOutput output, ref UpsertOptions upsertOptions, TContext userContext = default)\n            => throw new TsavoriteException(\"Transactional consistent read context does not allow writes!\");\n\n        /// <inheritdoc/>\n        [MethodImpl(MethodImplOptions.AggressiveInlining)]","sourceCodeStart":232,"sourceCodeEnd":268,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/storage/Tsavorite/cs/src/core/ClientSession/TransactionalConsistentReadContext.cs#L232-L268","documentation":"TransactionalConsistentReadContext rejects all write operations. This is the simplest Upsert overload (key, ReadOnlySpan<byte> value). Notably, this specific overload's message says 'Consistent read context does not allow writes!' (without the 'Transactional' prefix), unlike the neighboring overloads at lines 255+ which include 'Transactional'. This message inconsistency can mislead debugging — the throw originates from TransactionalConsistentReadContext but the message text matches ConsistentReadContext.","triggerScenarios":"Calling Upsert(TKey key, ReadOnlySpan<byte> desiredValue, TContext userContext) on session.TransactionalConsistentReadContext.","commonSituations":"Code that uses transactional consistent-read sessions for read-heavy transactional workloads and accidentally routes an upsert through the same context; the inconsistent message text causing confusion during debugging about which context is active.","solutions":["Call Upsert on session.TransactionalContext (for transactional writes with locking) or session.BasicContext instead.","Note the message text inconsistency: this throw comes from TransactionalConsistentReadContext even though the message omits 'Transactional'."],"exampleFix":"// before (throws)\nsession.TransactionalConsistentReadContext.Upsert(key, valueBytes);\n\n// after\nsession.TransactionalContext.Upsert(key, valueBytes);","handlingStrategy":"validation","validationCode":"// Route writes to TransactionalContext or BasicContext\n// Note: the message text says 'Consistent read context' but this is TransactionalConsistentReadContext\nsession.TransactionalContext.Upsert(key, valueBytes);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Be aware of the message-text inconsistency: line 250 says 'Consistent read context' (no 'Transactional') while neighboring overloads include 'Transactional'. When debugging, check the call stack to confirm the actual source context.","Keep TransactionalConsistentReadContext exclusively for Read operations."],"tags":["tsavorite","transactional","consistent-read","read-only-context","upsert","write-rejected","message-inconsistency","csharp"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}