{"record":{"id":"5fafb52c247c51ae","repo":"microsoft/garnet","slug":"transactional-consistent-read-context-does-not-all-5fafb5","errorCode":null,"errorMessage":"Transactional consistent read context does not allow writes!","messagePattern":"Transactional 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":255,"sourceCode":"        }\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)]\n        public Status Upsert(TKey key, ref TInput input, ReadOnlySpan<byte> desiredValue, ref TOutput output, ref UpsertOptions upsertOptions, out RecordMetadata recordMetadata, TContext userContext = default)\n            => throw new TsavoriteException(\"Transactional consistent read context does not allow writes!\");\n\n        public Status Upsert<TOpKey, TSourceLogRecord>(TOpKey key, in TSourceLogRecord diskLogRecord)\n            where TOpKey : IKey","sourceCodeStart":237,"sourceCodeEnd":273,"githubUrl":"https://github.com/microsoft/garnet/blob/951b0fc6838721f89d102c2bbe1b914e8d39d700/libs/storage/Tsavorite/cs/src/core/ClientSession/TransactionalConsistentReadContext.cs#L237-L273","documentation":"TransactionalConsistentReadContext rejects writes to preserve its snapshot-isolation guarantee within a transactional session. This overload accepts UpsertOptions (for TTL, ETag, etc.). The message correctly includes 'Transactional' prefix ('Transactional consistent read context does not allow writes!'), unlike the simpler overload at line 250. The throw is unconditional.","triggerScenarios":"Calling Upsert(TKey key, ReadOnlySpan<byte> desiredValue, ref UpsertOptions upsertOptions, TContext userContext) on session.TransactionalConsistentReadContext.","commonSituations":"Transactional read-heavy code paths that share a context variable and accidentally route upsert-with-options through the consistent-read transactional context.","solutions":["Call this Upsert overload on session.TransactionalContext or session.BasicContext, not on TransactionalConsistentReadContext.","Maintain clear separation: use TransactionalConsistentReadContext for reads-with-locking, TransactionalContext for writes-with-locking."],"exampleFix":"// before (throws)\nsession.TransactionalConsistentReadContext.Upsert(key, valueBytes, ref upsertOpts);\n\n// after\nsession.TransactionalContext.Upsert(key, valueBytes, ref upsertOpts);","handlingStrategy":"validation","validationCode":"// Use TransactionalContext for transactional writes with options\nsession.TransactionalContext.Upsert(key, valueBytes, ref upsertOptions);","typeGuard":null,"tryCatchPattern":null,"preventionTips":["Maintain separate context references: TransactionalConsistentReadContext for reads, TransactionalContext for writes within the same transaction.","Document which context each code path should use to prevent accidental write-through of the read context."],"tags":["tsavorite","transactional","consistent-read","read-only-context","upsert","upsert-options","write-rejected","csharp"],"backgroundTag":null,"analyzedSha":"951b0fc6838721f89d102c2bbe1b914e8d39d700","analyzedAt":"2026-08-13T19:01:32.939Z","schemaVersion":2},"datasetVersion":"2026-08-13T19:17:28.613Z"}