crewAIInc/crewAI · error · SystemExit

Error. A valid pyproject.toml file is required. Check that a

Error message

Error. A valid pyproject.toml file is required. Check that a valid pyproject.toml file exists in the current directory.

What it means

Raised as an HTTP 503 when an A2A server using OAuth2ServerAuth with an introspection_url cannot complete RFC 7662 token introspection. The introspection endpoint returned an HTTP error or the request failed unexpectedly (network error, timeout, bad credentials, malformed JSON). The server deliberately maps infrastructure failures to 503 instead of 401 to distinguish 'token bad' from 'cannot verify token'.

Source

Thrown at lib/cli/src/crewai_cli/cli.py:118


@click.group()
@click.version_option(_get_cli_version())
def crewai() -> None:
    """Top-level command group for crewai."""


@crewai.command(
    name="uv",
    context_settings={"ignore_unknown_options": True},
)
@click.argument("uv_args", nargs=-1, type=click.UNPROCESSED)
def uv(uv_args: tuple[str, ...]) -> None:
    """A wrapper around uv commands that adds custom tool authentication through env vars."""
    try:
        read_toml()
    except FileNotFoundError as e:
        raise SystemExit(
            "Error. A valid pyproject.toml file is required. Check that a valid pyproject.toml file exists in the current directory."
        ) from e
    except Exception as e:
        raise SystemExit(f"Error: {e}") from e

    env = build_env_with_all_tool_credentials()

    try:
        subprocess.run(  # noqa: S603
            ["uv", *uv_args],  # noqa: S607
            capture_output=False,
            env=env,
            text=True,
            check=True,
        )
    except subprocess.CalledProcessError as e:
        click.secho(f"uv command failed with exit code {e.returncode}", fg="red")
        raise SystemExit(e.returncode) from e

View on GitHub (pinned to 754d7323be)

Solutions

  1. Verify the introspection endpoint is reachable from the server host: curl -u client_id:client_secret -d 'token=<tok>' <introspection_url> and confirm a 200 with JSON containing "active"
  2. Check introspection_client_id and introspection_client_secret match credentials registered with the IdP (most IdPs require basic auth on the introspection endpoint)
  3. Confirm introspection_url is a fully qualified https URL to the RFC 7662 endpoint (e.g. https://idp.example.com/oauth2/introspect, not the token or userinfo endpoint)
  4. Inspect server logs: the 'OAuth2 introspection failed' log record includes reason (http_error vs unexpected_error), status_code, and error text pinpointing the layer that failed
  5. If failures are transient (IdP briefly restarting), treat 503 as retryable on the client side with backoff

Example fix

# before
OAuth2ServerAuth(
    introspection_url="https://idp.example.com/oauth2/introspect",  # wrong creds / endpoint
)

# after
OAuth2ServerAuth(
    introspection_url="https://idp.example.com/oauth2/introspect",
    introspection_client_id="my-client",
    introspection_client_secret=SecretStr(os.environ["INTROSPECTION_SECRET"]),  # verified via curl first
)
Defensive patterns

Strategy: retry

Validate before calling

import httpx

async def introspection_endpoint_healthy(url: str, client_id: str, client_secret: str) -> bool:
    try:
        async with httpx.AsyncClient(timeout=5) as c:
            r = await c.post(url, data={"token": "healthcheck-dummy"}, auth=(client_id, client_secret))
            return r.status_code == 200 and isinstance(r.json(), dict)
    except httpx.HTTPError:
        return False

Try / catch

from fastapi import HTTPException

try:
    user = await scheme.authenticate(request)
except HTTPException as e:
    if e.status_code == 503:  # introspection unavailable -> retryable
        raise RetryableUpstreamError("IdP introspection down") from e
    raise  # 401s are not retryable

Prevention

When it happens

Trigger: Calling an A2A endpoint protected by OAuth2ServerAuth configured with introspection_url while: the identity provider is down or unreachable; introspection_client_id/secret are wrong so the IdP returns 4xx; the URL is misconfigured (DNS failure, TLS error, non-HTTP2 JSON body); or httpx raises any non-HTTPStatusError exception (connect timeout, ReadTimeout) inside _authenticate_introspection (server_schemes.py:587-619).

Common situations: IdP outage or maintenance window; introspection endpoint behind a VPN/firewall the server cannot reach; client credentials rotated but the CrewAI auth config was not updated; typo in introspection_url; self-signed certificate rejected by httpx.

Related errors


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