Challenge 3: A Custom Exception for a Missing Task — Possible Solution ==================================================================== from tasks.models import Task class TaskNotFoundError(Exception): pass class TaskStorage: # ... __init__, load, save unchanged from the chapter ... def find(self, tasks: list[Task], task_id: int) -> Task: for task in tasks: if task.id == task_id: return task raise TaskNotFoundError(f"No task found with id {task_id}") # usage example: storage = TaskStorage(...) tasks = storage.load() try: task = storage.find(tasks, 99) except TaskNotFoundError as e: print(f"Error: {e}") Output (assuming no task with id 99 exists): Error: No task found with id 99 WHY THIS WORKS AS AN ANSWER ------------------------------ class TaskNotFoundError(Exception): pass reuses the exact custom- exception pattern from Course 2, Chapter 3's own InsufficientFundsError example — a class inheriting from Exception, with pass as the entire body being enough to make it a real, distinct, catchable type. find(self, tasks: list[Task], task_id: int) -> Task: reuses this course's own Chapter 5 type-hint conventions directly — list[Task] and task_id: int as parameter hints, -> Task as the return type, documenting that this method either returns a real Task or doesn't return at all (it raises instead). The for loop with if task.id == task_id: return task reuses ordinary list iteration and comparison to search for a matching task, returning immediately the moment a match is found — the same "search, return early on match" shape as many earlier challenges in this course. raise TaskNotFoundError(f"No task found with id {task_id}") only runs if the loop completes without ever finding a match — reusing the f-string-with-descriptive-message pattern from Course 2, Chapter 3's own custom exception example, so the error message identifies exactly which id was missing rather than a generic failure. try/except TaskNotFoundError as e: at the call site reuses Course 2, Chapter 3's exception-catching mechanics precisely, catching this specific custom type by name rather than a generic Exception — making the calling code's intent (specifically handling a missing-task case) immediately clear from the except clause itself.