shareAI-lab/learn-claude-code · error · ValueError

Task {task_id} not found

Error message

Task {task_id} not found

What it means

Raised by TaskManager._load() in agents/s07_task_system.py:60 when a task JSON file for the given id does not exist under tasks_dir. Ids map 1:1 to files task_<id>.json; _max_id() bootstraps the next id from existing filenames at startup, so any get/update/remove call with an id that was never created — or whose file was deleted externally — fails here.

Source

Thrown at agents/s07_task_system.py:60

SYSTEM = f"You are a coding agent at {WORKDIR}. Use task tools to plan and track work."


# -- TaskManager: CRUD with dependency graph, persisted as JSON files --
class TaskManager:
    def __init__(self, tasks_dir: Path):
        self.dir = tasks_dir
        self.dir.mkdir(exist_ok=True)
        self._next_id = self._max_id() + 1

    def _max_id(self) -> int:
        ids = [int(f.stem.split("_")[1]) for f in self.dir.glob("task_*.json")]
        return max(ids) if ids else 0

    def _load(self, task_id: int) -> dict:
        path = self.dir / f"task_{task_id}.json"
        if not path.exists():
            raise ValueError(f"Task {task_id} not found")
        return json.loads(path.read_text())

    def _save(self, task: dict):
        path = self.dir / f"task_{task['id']}.json"
        path.write_text(json.dumps(task, indent=2, ensure_ascii=False))

    def create(self, subject: str, description: str = "") -> str:
        task = {
            "id": self._next_id, "subject": subject, "description": description,
            "status": "pending", "blockedBy": [], "owner": "",
        }
        self._save(task)
        self._next_id += 1
        return json.dumps(task, indent=2, ensure_ascii=False)

    def get(self, task_id: int) -> str:
        return json.dumps(self._load(task_id), indent=2, ensure_ascii=False)

View on GitHub (pinned to 985456f4ad)

Solutions

  1. Call task_list/task_next first and use ids from its output, never guessed ids
  2. After wiping or moving the tasks dir, restart the conversation or re-create tasks — old ids are invalid
  3. Treat this error as terminal for that id: do not retry the same id; pick an existing one

Example fix

# before
tasks.update(task_id=7, status="in_progress")
# ValueError: Task 7 not found

# after
existing = json.loads(tasks.list())
ids = [t["id"] for t in existing if t["status"] == "pending"]
if ids:
    tasks.update(task_id=ids[0], status="in_progress")
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path

def task_exists(tasks_dir: Path, task_id: int) -> bool:
    return (tasks_dir / f"task_{task_id}.json").exists()

# before get/update:
assert task_exists(TASKS_DIR, task_id), f"Task {task_id} not found; call task_list for valid ids"

Type guard

def is_valid_task_id(task_id: object, tasks_dir) -> bool:
    if not isinstance(task_id, int) or isinstance(task_id, bool) or task_id < 1:
        return False
    return (tasks_dir / f"task_{task_id}.json").exists()

Try / catch

try:
    TASKS.update(task_id, status)
except ValueError as e:
    if "not found" in str(e):
        valid = TASKS.list()
        return f"Task {task_id} no longer exists. Current tasks:\n{valid}"
    raise

Prevention

When it happens

Trigger: Calling task_get/task_update with a guessed or stale id (e.g. after the tasks dir was wiped between runs, so old ids no longer exist), a deleted task, or an id larger than the max seen by list_tasks(). Note _max_id() does int(f.stem.split("_")[1]) without a try/except here, so a manually created task file with a non-numeric suffix would crash earlier at startup rather than here.

Common situations: Agent resumes a session with persisted conversation but a cleaned tasks dir. Model hallucinates an id instead of calling task_list first. Two harness instances sharing one tasks_dir racing on file deletion.

Related errors


AI-assisted analysis of shareAI-lab/learn-claude-code@985456f4ad (2026-08-14). Data as JSON: /api/errors/14de6dd5eabe3661. Report an issue: GitHub.