-
Type:
Bug
-
Resolution: Fixed
-
Priority:
Major - P3
-
Affects Version/s: None
-
Component/s: Change streams
-
Query Execution
-
Fully Compatible
-
ALL
-
QE 2026-06-22, QE 2026-07-06
-
None
-
None
-
None
-
None
-
None
-
None
-
None
The following issue was surfaced by Claude Code. It only becomes a problem when the feature flag for endOfTransaction is enabled. The feature flag is currently disabled by default, but we may want to enable it in a future version.
Problem:
When the endOfTransaction feature flag (gFeatureFlagEndOfTransactionChangeEvent) is enabled and a multi-document transaction unwinds, _addAffectedNamespaces is called for every operation in the applyOps array. For command-type operations (op == "c"), the code only handles "create" and "createIndexes" and unconditionally tasserts on anything else:
void ChangeStreamUnwindTransactionStage::TransactionOpIterator::_addAffectedNamespaces( const Document& doc) { ... if (doc["op"_sd].getStringData() != "c"_sd) { _affectedNamespaces.insert(dbCmdNs); return; } constexpr std::array<StringData, 2> kCollectionField = {"create"_sd, "createIndexes"_sd}; const Document& object = doc["o"_sd].getDocument(); for (const auto& fieldName : kCollectionField) { const auto field = object[fieldName]; if (field.getType() == BSONType::string) { _affectedNamespaces.insert(...); return; } } tasserted(7694300, // ← fires for any "c" op that isn't create/createIndexes str::stream() << "Unexpected op in applyOps at " << _currentApplyOpsTs << " index " << _currentApplyOpsIndex << " object " << redact(object.toString())); }
In MongoDB 8.0+ DDL-in-transactions work (e.g. creating a timeseries collection inside a transaction via db.createCollection("ts", {timeseries: {...}})) can emit command entries in applyOps whose o field contains neither create nor createIndexes but something like createTimeseries or other internal sub-commands written by newer server DDL machinery. Any such entry matching the change stream's namespace filter reaches this code path and fires tassert(7694300).
tassert throws a tripwire assertion, causing the current change stream getMore to fail. The stream becomes non-resumable until the problematic transaction clears the oplog window.
Potential fix:
Investigate which inner command operations can appear inside an applyOps oplog entry, apart from the already handled DDL events create and createIndexes, then add these to the existing kCollection variable.
Add a JavaScript integration test that requires the featureFlagEndOfTransactionChangeEvent and that embeds the new event types as well as create and createIndexes inside a transactional applyOps oplog entry, and verify that a change stream opened on it can successfully consume the transactional events including endOfTransaction, but does not tassert with id 7694300.