redis/redis-py · error · NotImplementedError

Method is not supported in transactional context.

Error message

Method is not supported in transactional context.

What it means

Raised as NotImplementedError by ClusterPipeline.mset_nonatomic. The cluster pipeline is itself transactional (MULTI/EXEC on one node), so the non-atomic MSET helper — which fans out across slots as independent writes — has no meaningful implementation in this context.

Source

Thrown at redis/asyncio/cluster.py:3320

        for r, cmd in zip(responses, stack):
            if isinstance(r, Exception):
                self._annotate_exception(r, cmd.position + 1, cmd.args)

                await record_operation_duration(
                    command_name="TRANSACTION",
                    duration_seconds=time.monotonic() - start_time,
                    server_address=self._transaction_connection.host,
                    server_port=self._transaction_connection.port,
                    db_namespace=str(self._transaction_connection.db),
                    error=r,
                )

                raise r

    def mset_nonatomic(
        self, mapping: Mapping[AnyKeyT, EncodableT]
    ) -> "ClusterPipeline":
        raise NotImplementedError("Method is not supported in transactional context.")

    async def execute(
        self, raise_on_error: bool = True, allow_redirections: bool = True
    ) -> List[Any]:
        stack = self._command_queue
        if not stack and (not self._watching or not self._pipeline_slots):
            return []

        return await self._execute_transaction_with_retries(stack, raise_on_error)

    async def _execute_transaction_with_retries(
        self, stack: List["PipelineCommand"], raise_on_error: bool
    ):
        return await self._retry.call_with_retry(
            lambda: self._execute_transaction(stack, raise_on_error),
            lambda error, failure_count: self._reinitialize_on_error(
                error, failure_count
            ),

View on GitHub (pinned to da03cdc7e8)

Solutions

  1. Call mset_nonatomic on the non-transactional cluster pipeline: client.pipeline(transaction=False) (or the regular client) instead of the transactional one.
  2. Use plain mset() (with hash tags if needed) inside the transactional pipeline, or split the mapping into per-slot transactions.
  3. If you need cross-slot non-atomic mset, use the async cluster client directly: await client.mset_nonatomic(mapping).

Example fix

// before
pipe = client.pipeline(transaction=True)
await pipe.mset_nonatomic(mapping)  # raises [85]
// after
await client.mset_nonatomic(mapping)  # use the client, not the txn pipeline
Defensive patterns

Strategy: validation

Validate before calling

if pipe._transaction:
    raise ValueError('Use client.mset_nonatomic, not the transactional pipeline')

Try / catch

try:
    await pipe.mset_nonatomic(mapping)
except NotImplementedError:
    await client.mset_nonatomic(mapping)

Prevention

When it happens

Trigger: Calling await pipe.mset_nonatomic({...}) on a cluster pipeline object (the transactional pipeline returned by client.pipeline()).

Common situations: Reusing mset_nonatomic from the non-transactional ClusterPipeline API on a transactional pipeline, or copy-pasting code that worked on the async cluster client's pipeline() result.

Related errors


AI-assisted analysis of redis/redis-py@da03cdc7e8 (2026-08-04). Data as JSON: /data/errors/291124f02087d75b.json. Report an issue: GitHub.