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

Dependency not found: {dependency}

Error message

Dependency not found: {dependency}

What it means

create_task(blockedBy=[...]) validates every dependency ID by calling _task_path(dependency).is_file() — the dependency's JSON must already exist in .tasks/. This prevents tasks from referencing nonexistent predecessors, which would deadlock scheduling since can_start() waits on those dependencies completing.

Source

Thrown at s13_agent_teams/code.py:142

    if not isinstance(task_id, str) or not TASK_ID_PATTERN.fullmatch(task_id):
        raise ValueError(f"Invalid task ID: {task_id!r}")
    path = (TASKS_DIR / f"{task_id}.json").resolve()
    if (not TASKS_ROOT.is_relative_to(WORKDIR.resolve())
            or not path.is_relative_to(TASKS_ROOT)):
        raise ValueError(f"Invalid task ID: {task_id!r}")
    return path


def create_task(subject: str, description: str = "",
                blockedBy: list[str] | None = None) -> Task:
    subject = subject.strip()
    if not subject:
        raise ValueError("Task subject cannot be empty")
    dependencies = list(dict.fromkeys(blockedBy or []))
    with task_store_lock():
        for dependency in dependencies:
            if not _task_path(dependency).is_file():
                raise ValueError(f"Dependency not found: {dependency}")
        for _ in range(100):
            task = Task(
                id=f"task_{secrets.token_hex(4)}",
                subject=subject,
                description=description,
                status="pending",
                owner=None,
                blockedBy=dependencies,
            )
            try:
                with _task_path(task.id).open("x", encoding="utf-8") as handle:
                    json.dump(asdict(task), handle, indent=2)
                return task
            except FileExistsError:
                continue
    raise RuntimeError("Could not allocate a unique task ID")

View on GitHub (pinned to 985456f4ad)

Solutions

  1. Create dependency tasks first and use their returned Task.id values for blockedBy.
  2. Deduplicate/validate IDs against list_tasks() before calling create_task.
  3. In tests, build the dependency chain in order rather than in parallel threads.

Example fix

// before
create_task('deploy', blockedBy=['task_deadbeef'])  // may not exist

// after
build = create_task('build')
deploy = create_task('deploy', blockedBy=[build.id])
Defensive patterns

Strategy: validation

Validate before calling

def deps_exist(dependency_ids: list[str]) -> bool:
    from pathlib import Path
    return all((TASKS_DIR / f'{d}.json').is_file() for d in dependency_ids)

# before create_task:
assert deps_exist(blockedBy or [])

Try / catch

try:
    task = create_task(subject, blockedBy=deps)
except ValueError as exc:
    if str(exc).startswith('Dependency not found'):
        deps = [d for d in deps if (TASKS_DIR / f'{d}.json').is_file()]
        task = create_task(subject, blockedBy=deps)  # or report upstream
    else:
        raise

Prevention

When it happens

Trigger: create_task('x', blockedBy=['task_00000000']) when no such file exists; passing an ID with a typo; passing dependency IDs created in another workspace; a race where the dependency task was created but its file write failed.

Common situations: Copy-pasting task graphs between workspaces; races in tests that create dependent tasks concurrently; stale hardcoded IDs from a previous run (IDs are random hex per run).

Related errors


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