Oh only now it appears in the list ! I thought the post hadn't working, so I posted again :/. I've fixed my "library" (https://github.com/aure-olli/aiokafka/blob/3acb88d6ece4502a78e230b234f47b90b...), and the `wrapped_consumer2` function. Now no double await, so no risk of afterward cancellation. stop_future = asyncio.Future() async def wrapped_consumer2(): task = asyncio.ensure_future(consume_data()) try: await asyncio.wait([task, stop_future]) finally: task.cancel() if not task.cancelled(): return task.result() else: raise RuntimeError('stopped') Or import syncio async def wrapped_consumer(): task = syncio.ensure_sync_future(consume_data()) return await task stop_future = asyncio.Future() async def wrapped_consumer2(): task = syncio.ensure_sync_future(consume_data()) try: await syncio.sync_wait([task, stop_future]) finally: task.cancel() if not task.cancelled(): return task.result() else: raise RuntimeError('stopped') My only concern is that consistent cancellation state is currently almost impossible with futures and tasks. The only two possibilities are either to ignore cancellation (highly counter intuitive to use), or to directly manipulate the coroutine as a generator (basically rewriting asyncio). Could be another library for sure, but the current state of asyncio makes it really hard to reuse it. So it would mean copy-pasting the whole library while changing few lines here and there.