Unhandled exception in asyncio.create_task(): consequence and detection

Tests Task exception capture vs propagation. Good answer: exceptions are stored in the Task object, the loop keeps running, and the creator must await the task or call task.exception() to retrieve it; unretrieved ones may be logged.
WHAT THIS TESTS: Whether you understand the Task exception lifecycle and the fire-and-forget anti-pattern in asyncio. The interviewer cares if you know that create_task schedules a coroutine as a background Task but does not automatically propagate exceptions back to the caller. They want to see that you understand the difference between scheduling a task and awaiting it, and that unretrieved exceptions can be silently lost or logged as warnings depending on the Python version and debug mode.
A GOOD ANSWER COVERS: Four things in order. First, the immediate consequence is that the unhandled exception is captured and stored inside the Task object. The event loop is not crashed; other tasks continue running normally. Second, the part of the code that created the task can reliably detect the failure by awaiting the task later, which will re-raise the stored exception, or by calling task.result() or task.exception() after confirming task.done() is True. Third, if the task reference is dropped and the exception is never retrieved, asyncio may log a warning about a never-retrieved exception, which is why purely fire-and-forget tasks are dangerous in production. Fourth, a robust pattern is to maintain a collection of active tasks and clean them up during shutdown, or to add a done callback that inspects task.exception() and handles or logs it explicitly.
COMMON WRONG ANSWERS: Claiming that an unhandled exception in a background task crashes the event loop or terminates the program. Saying that asyncio.create_task silently swallows exceptions with no retrieval mechanism. Suggesting sys.excepthook or threading-style exception handling as the primary solution instead of awaiting the Task object. Proposing that wrapping the coroutine body in a broad try/except is the only way to detect failures, which misses the built-in Task exception storage.
LIKELY FOLLOW-UPS: How would you handle exceptions in a long-running service like FastAPI that fires off many background tasks? What changed in recent Python versions regarding the logging of unretrieved task exceptions? How do modern APIs like asyncio.TaskGroup address this problem compared to raw create_task? How do you prevent memory leaks from accumulated task references while still detecting failures?
ONE CONCRETE EXAMPLE: Imagine a FastAPI endpoint that receives a file upload and calls asyncio.create_task(process_file(file)) so it can return a 202 Accepted response immediately. If process_file later raises an OSError because the disk is full, the exception is stored in the Task object. The endpoint has already responded, so it does not see the failure. To detect it reliably, the application must keep a reference to that task in a registry or global set. During application shutdown or a periodic cleanup sweep, it can iterate the set, remove done tasks, and call task.exception() on each to log or alert on failures. If the application simply drops the reference, the failure may go completely unnoticed until a never-retrieved exception warning appears in the logs, if at all.
Read the original → docs.python.org
Get five bites like this every day.
Tezvyn delivers a daily feed of 60-second tech bites with quizzes to lock in what you learn.