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

Invalid task status: {task.status}

Error message

Invalid task status: {task.status}

What it means

load_task() enforces a closed status vocabulary: {'pending', 'in_progress', 'completed'}. The Task dataclass annotates status as a bare str, so any other string (or a status written by a newer/older version) is rejected at load time rather than silently flowing into scheduling logic.

Source

Thrown at s13_agent_teams/code.py:183

            f".{path.name}.{os.getpid()}.{threading.get_ident()}.tmp"
        )
        try:
            temporary.write_text(
                json.dumps(asdict(task), indent=2), encoding="utf-8"
            )
            os.replace(temporary, path)
        finally:
            temporary.unlink(missing_ok=True)


def load_task(task_id: str) -> Task:
    with task_lock:
        data = json.loads(_task_path(task_id).read_text(encoding="utf-8"))
        task = Task(**data)
        if task.id != task_id:
            raise ValueError(f"Task file ID does not match {task_id}")
        if task.status not in {"pending", "in_progress", "completed"}:
            raise ValueError(f"Invalid task status: {task.status}")
        return task


def list_tasks() -> list[Task]:
    with task_lock:
        if not TASKS_DIR.exists():
            return []
        if not TASKS_ROOT.is_relative_to(WORKDIR.resolve()):
            raise ValueError("Tasks directory escapes workspace")
        return [load_task(path.stem)
                for path in sorted(TASKS_DIR.glob("task_*.json"))]


def get_task(task_id: str) -> str:
    """Return full task details as JSON."""
    task = load_task(task_id)
    return json.dumps(asdict(task), indent=2)

View on GitHub (pinned to 985456f4ad)

Solutions

  1. Use exactly one of 'pending', 'in_progress', 'completed' (lowercase, underscore).
  2. Update test fixtures and scripts that write informal statuses like 'done'.
  3. If a new status is genuinely needed, add it to the set in load_task() everywhere the vocabulary is checked.

Example fix

// before
save_task(replace_status(task, 'done'))  // next load_task raises

// after
save_task(replace_status(task, 'completed'))
Defensive patterns

Strategy: validation

Validate before calling

VALID_STATUSES = {'pending', 'in_progress', 'completed'}

def valid_status(status: object) -> bool:
    return status in VALID_STATUSES

Type guard

from typing import Literal, TypeGuard
TaskStatus = Literal['pending', 'in_progress', 'completed']

def is_task_status(value: object) -> TypeGuard[TaskStatus]:
    return value in {'pending', 'in_progress', 'completed'}

Try / catch

try:
    task = load_task(task_id)
except ValueError as exc:
    if 'Invalid task status' in str(exc):
        log_and_quarantine(task_id)  # move file aside for inspection
        return None
    raise

Prevention

When it happens

Trigger: Hand-editing .tasks/*.json to 'done', 'Done', 'in-progress' (hyphen), or 'blocked'; a newer version of the system writing a new status like 'failed' that this version doesn't know; serializing an enum instead of its value.

Common situations: Manual status edits; version skew between components sharing the .tasks directory; test fixtures written with informal status names.

Related errors


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