crewAIInc/crewAI · error · ProjectDefinitionError

Invalid project root for [tool.crewai] definition: {exc}

Error message

Invalid project root for [tool.crewai] definition: {exc}

What it means

Raised when Path.resolve(strict=True) on the project root raises OSError, meaning the root passed to resolve_project_definition_path does not exist or cannot be stat'd (missing directory, permission denied, broken symlink, or a path component that is a file). The definition path itself has not been evaluated yet; the problem is the root. The original OSError is chained via 'from exc'.

Source

Thrown at lib/crewai-core/src/crewai_core/project.py:119

    definition_path = Path(definition)
    windows_definition_path = PureWindowsPath(definition)

    if definition.startswith("~"):
        raise ProjectDefinitionError(
            "[tool.crewai] definition must be a project-local path; "
            f"got {definition!r}."
        )

    if definition_path.is_absolute() or windows_definition_path.is_absolute():
        raise ProjectDefinitionError(
            "[tool.crewai] definition must be relative to the project root; "
            f"got {definition!r}."
        )

    try:
        root = root_path.resolve(strict=True)
    except OSError as exc:
        raise ProjectDefinitionError(
            f"Invalid project root for [tool.crewai] definition: {exc}"
        ) from exc

    candidate = root / definition_path
    try:
        resolved_candidate = candidate.resolve(strict=False)
    except OSError as exc:
        raise ProjectDefinitionError(
            f"Invalid [tool.crewai] definition path {definition!r}: {exc}"
        ) from exc

    if not resolved_candidate.is_relative_to(root):
        raise ProjectDefinitionError(
            "[tool.crewai] definition must resolve inside the project root; "
            f"got {definition!r}."
        )

    if not resolved_candidate.exists():

View on GitHub (pinned to 754d7323be)

Solutions

  1. Check the project root exists and is a directory before calling the resolver: Path(project_root).is_dir().
  2. Print or log the exact root value being passed; it usually differs from what you expect in CI or when cwd changed.
  3. Derive the root from a known anchor (the pyproject.toml location) instead of assuming cwd.

Example fix

# before
root = os.environ.get("CREW_ROOT", "/opt/crew")  # may not exist
path = resolve_project_definition_path(definition, root)

# after
root = Path(__file__).resolve().parent.parent  # anchor to this file
path = resolve_project_definition_path(definition, root)
Defensive patterns

Strategy: validation

Validate before calling

from pathlib import Path

def valid_root(project_root: str | Path) -> bool:
    p = Path(project_root)
    return p.exists() and p.is_dir() and p.resolve(strict=True) is not None

Try / catch

try:
    path = resolve_project_definition_path(definition, root)
except ProjectDefinitionError as e:
    if "Invalid project root" in str(e):
        # root problem, not definition problem
        ...

Prevention

When it happens

Trigger: Calling resolve_project_definition_path(definition="main.py", project_root="/nonexistent/dir") or with a root the process cannot read; also when the root contains a symlink loop or the passed root is actually a file path.

Common situations: Hardcoding a root in CI that differs between runners, running from a deleted/renamed working directory, or passing repo-root detection output (e.g. git rev-parse) that returned empty and got joined into an invalid path.

Related errors


AI-assisted analysis of crewAIInc/crewAI@754d7323be (2026-08-15). Data as JSON: /api/errors/ec3394c715ae0a0a. Report an issue: GitHub.