Mark a task as failed, with retry logic. If retries remain, sets task back to pending.
(self, task_id: int, error_msg: str)
| 226 | return True |
| 227 | |
| 228 | def fail_task(self, task_id: int, error_msg: str) -> bool: |
| 229 | """ |
| 230 | Mark a task as failed, with retry logic. |
| 231 | |
| 232 | If retries remain, sets task back to pending. |
| 233 | """ |
| 234 | task = self._tasks.get(task_id) |
| 235 | if not task: |
| 236 | return False |
| 237 | |
| 238 | task.retry_count += 1 |
| 239 | self.state.increment_retry(task_id) |
| 240 | |
| 241 | if task.retry_count < self.max_retries: |
| 242 | # Retry - set back to pending |
| 243 | task.status = TaskStatus.PENDING |
| 244 | task.error = f"Retry {task.retry_count}/{self.max_retries}: {error_msg}" |
| 245 | self.state.execute( |
| 246 | "UPDATE task_queue SET status = 'pending' WHERE id = ?", (task_id,) |
| 247 | ) |
| 248 | warning(f"Planner: task {task_id} failed, retrying ({task.retry_count}/{self.max_retries})") |
| 249 | else: |
| 250 | # Max retries reached - mark as failed |
| 251 | task.status = TaskStatus.FAILED |
| 252 | task.error = error_msg |
| 253 | task.completed_at = int(time.time()) |
| 254 | |
| 255 | # Update database |
| 256 | self.state.fail_task(task_id, error_msg) |
| 257 | |
| 258 | error(f"Planner: task {task_id} failed permanently: {error_msg}") |
| 259 | |
| 260 | # Log in episodic memory |
| 261 | self.state.log_action("task_failed", f"Task {task_id}: {error_msg[:200]}") |
| 262 | |
| 263 | # Clear current task |
| 264 | if self._current_task and self._current_task.id == task_id: |
| 265 | self._current_task = None |
| 266 | |
| 267 | return True |
| 268 | |
| 269 | def adapt(self, task_id: int, error_msg: str) -> Optional[str]: |
| 270 | """ |
nothing calls this directly
no test coverage detected