{"record":{"id":"c83543be03f3d8e4","repo":"langflow-ai/langflow","slug":"invalid-api-key","errorCode":null,"errorMessage":"Invalid API key","messagePattern":"Invalid API key","errorType":"http","errorClass":"HTTPException","httpStatus":401,"severity":"error","filePath":"src/backend/base/langflow/api/v1/a2a.py","lineNumber":136,"sourceCode":"    # Short writable session (check_key flushes usage counters), closed before\n    # dispatch so no lock is held across the up-to-300s run.\n    async with session_scope() as session:\n        auth_type = await folder_auth_type(flow, session)\n        if auth_type == \"none\":\n            return  # public agent\n        if auth_type not in (\"apikey\", \"oauth\"):\n            # Protected folder with a scheme A2A can't enforce: fail closed, never public.\n            raise HTTPException(\n                status_code=status.HTTP_403_FORBIDDEN,\n                detail=f\"A2A access is disabled for this agent: unsupported folder auth type {auth_type!r}.\",\n            )\n        api_key = request.headers.get(A2A_APIKEY_HEADER)\n        if not api_key:\n            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=\"API key required\")\n        user = await check_key(session, api_key)\n        # Same message for invalid and wrong-owner: don't reveal a key is valid for another user.\n        if user is None or user.id != flow.user_id:\n            raise HTTPException(status_code=status.HTTP_401_UNAUTHORIZED, detail=\"Invalid API key\")\n\n\nclass _FlowContextBuilder(DefaultServerCallContextBuilder):\n    \"\"\"Carry the per-request flow_id into the shared executor via call-context state.\"\"\"\n\n    def build(self, request: Request) -> ServerCallContext:\n        context = super().build(request)\n        # Canonicalize to the same string form the resume guard uses (str(UUID(...))), so the durable\n        # store scope (_task_scope / _push_config_scope) and the checkpoint/resume guard agree even for\n        # a non-canonical UUID in the path (uppercase or hyphenless, both valid to the UUID route\n        # converter). Without this, the same task addressed via two encodings lands in two scopes.\n        context.state[\"flow_id\"] = str(UUID(request.path_params[\"flow_id\"]))\n        return context\n\n\nasync def _is_public_a2a_flow(flow: Flow) -> bool:\n    \"\"\"Whether an A2A flow admits callers without an API key.\"\"\"\n    async with session_scope_readonly() as session:","sourceCodeStart":118,"sourceCodeEnd":154,"githubUrl":"https://github.com/langflow-ai/langflow/blob/976ec789d2886a86de109c044d089d68e96c9a35/src/backend/base/langflow/api/v1/a2a.py#L118-L154","documentation":"Raised by _enforce_a2a_auth when the x-api-key is presented but either fails check_key (unknown/disabled key) or belongs to a different user than the flow owner. The same message covers both cases intentionally: revealing 'valid key, wrong user' would disclose that the key works elsewhere. The key must be issued by the flow's owner account.","triggerScenarios":"POST /api/v1/a2a/{flow_id}/jsonrpc with an x-api-key that is revoked, mistyped, from another user's account, or a key created after the server cached key state.","commonSituations":"Using an admin/personal key from a different account than the one that owns the agent flow; copying a key from another environment; a key revoked or rotated after the client was configured.","solutions":["Regenerate an API key under the flow owner's account and use that in x-api-key","Verify the key still exists and is active in the owner's API-keys page (it may have been revoked)","Confirm flow ownership — if the flow belongs to user A, only user A's keys work; transfer the flow or use the owner's key"],"exampleFix":"# before\nheaders={\"x-api-key\": os.environ[\"ADMIN_API_KEY\"]}\n# after\nheaders={\"x-api-key\": os.environ[\"FLOW_OWNER_API_KEY\"]}","handlingStrategy":"validation","validationCode":"import httpx\n\ndef key_is_live(base_url: str, api_key: str, auth: str) -> bool:\n    r = httpx.get(f\"{base_url}/api/v1/api_keys/\", headers={\"x-api-key\": api_key}, auth=auth)\n    return r.status_code == 200  # a valid, active key lists fine","typeGuard":null,"tryCatchPattern":"try:\n    resp = await client.send_message(flow_id, payload)\nexcept A2AClientError as e:\n    if \"Invalid API key\" in str(e):\n        key = rotate_to_owner_key(flow_id)   # fetch/regenerate under the flow owner\n        client = rebuild_client_with_x_api_key(key)\n        resp = await client.send_message(flow_id, payload)\n    else:\n        raise","preventionTips":["Issue the A2A key from the same account that owns the agent flow","Store the flow_id and its owner-key together in secrets management as one unit","On 401, treat 'Invalid API key' as either revoked or wrong-owner — check both before retrying"],"tags":["a2a","auth","http-401","api-key"],"backgroundTag":null,"analyzedSha":"976ec789d2886a86de109c044d089d68e96c9a35","analyzedAt":"2026-08-14T18:23:12.227Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}