langflow-ai/langflow · error · HTTPException

API key required

Error message

API key required

What it means

Raised by _enforce_a2a_auth when the flow's folder uses apikey/oauth auth but the JSON-RPC request carries no x-api-key header (A2A_APIKEY_HEADER). Because the flow runs as its owner, an unauthenticated run would be a run under the owner's identity, so the request is rejected with 401 before dispatch. Note it deliberately uses check_key, not api_key_security, to avoid AUTO_LOGIN silently substituting the superuser for a missing key.

Source

Thrown at src/backend/base/langflow/api/v1/a2a.py:132

    Uses ``check_key`` directly, NOT ``api_key_security``: under AUTO_LOGIN the latter
    returns the superuser for a *missing* key, which would silently bypass this gate.
    """
    # Short writable session (check_key flushes usage counters), closed before
    # dispatch so no lock is held across the up-to-300s run.
    async with session_scope() as session:
        auth_type = await folder_auth_type(flow, session)
        if auth_type == "none":
            return  # public agent
        if auth_type not in ("apikey", "oauth"):
            # Protected folder with a scheme A2A can't enforce: fail closed, never public.
            raise HTTPException(
                status_code=status.HTTP_403_FORBIDDEN,
                detail=f"A2A access is disabled for this agent: unsupported folder auth type {auth_type!r}.",
            )
        api_key = request.headers.get(A2A_APIKEY_HEADER)
        if not api_key:
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="API key required")
        user = await check_key(session, api_key)
        # Same message for invalid and wrong-owner: don't reveal a key is valid for another user.
        if user is None or user.id != flow.user_id:
            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail="Invalid API key")


class _FlowContextBuilder(DefaultServerCallContextBuilder):
    """Carry the per-request flow_id into the shared executor via call-context state."""

    def build(self, request: Request) -> ServerCallContext:
        context = super().build(request)
        # Canonicalize to the same string form the resume guard uses (str(UUID(...))), so the durable
        # store scope (_task_scope / _push_config_scope) and the checkpoint/resume guard agree even for
        # a non-canonical UUID in the path (uppercase or hyphenless, both valid to the UUID route
        # converter). Without this, the same task addressed via two encodings lands in two scopes.
        context.state["flow_id"] = str(UUID(request.path_params["flow_id"]))
        return context

View on GitHub (pinned to 976ec789d2)

Solutions

  1. Send the flow owner's Langflow API key in the x-api-key header on every JSON-RPC request
  2. Confirm the flow's folder auth_type — if it should be public, set the folder auth to 'none'
  3. Use the exact header name the card advertises (x-api-key), not Authorization: Bearer

Example fix

# before
client = Client(url)  # no credentials
# after
client = Client(httpx_client=httpx.AsyncClient(headers={"x-api-key": OWNER_API_KEY}))
Defensive patterns

Strategy: validation

Validate before calling

def has_owner_api_key configured() -> bool:
    key = os.environ.get("FLOW_OWNER_API_KEY", "")
    return bool(key) and len(key) >= 20  # cheap presence check, no network call

Try / catch

try:
    resp = await client.send_message(flow_id, payload)
except A2AClientError as e:
    if "API key required" in str(e):
        client = rebuild_client_with_x_api_key(os.environ["FLOW_OWNER_API_KEY"])
        resp = await client.send_message(flow_id, payload)
    else:
        raise

Prevention

When it happens

Trigger: POST /api/v1/a2a/{flow_id}/jsonrpc (message/send, message/stream, etc.) to a flow in an apikey or oauth folder without an x-api-key header, e.g. a raw a2a-sdk client that only sets Authorization or no credentials at all.

Common situations: Testing the public-agent path against a flow that was later moved into an apikey folder; A2A clients that put credentials in a different header or a JSON-RPC field; assuming the agent card's security scheme is optional.

Related errors


AI-assisted analysis of langflow-ai/langflow@976ec789d2 (2026-08-14). Data as JSON: /api/errors/533c2f1680472fb6. Report an issue: GitHub.