{"record":{"id":"082073796195bcd0","repo":"langflow-ai/langflow","slug":"provider-account-is-already-tracked-by-user","errorCode":null,"errorMessage":"Provider account is already tracked by user.","messagePattern":"Provider account is already tracked by user\\.","errorType":"http","errorClass":"HTTPException","httpStatus":409,"severity":"error","filePath":"src/backend/base/langflow/api/v1/deployments.py","lineNumber":216,"sourceCode":"        ),\n    ),\n]\n\n\ndef _field_was_explicitly_set(model: object, field_name: str) -> bool:\n    \"\"\"Return True when a Pydantic-style model explicitly received *field_name*.\n\n    Falls back to False for mocks and non-Pydantic objects so route handler unit\n    tests that use ``MagicMock`` payloads keep their previous behavior.\n    \"\"\"\n    fields_set = getattr(model, \"model_fields_set\", None)\n    return isinstance(fields_set, set) and field_name in fields_set\n\n\ndef _raise_http_for_provider_account_value_error(exc: ValueError) -> None:\n    message = str(exc).lower()\n    if \"already exists\" in message or \"conflicts with an existing record\" in message:\n        raise HTTPException(\n            status_code=status.HTTP_409_CONFLICT,\n            detail=\"Provider account is already tracked by user.\",\n        ) from exc\n    raise_http_for_value_error(exc)\n\n\nasync def _count_provider_deployments_after_reconciliation(\n    *,\n    session: DbSession,\n    provider_account,\n    user_id: UUID,\n) -> int:\n    \"\"\"Return remaining deployments after best-effort stale-row reconciliation.\"\"\"\n    deployment_count = await count_deployments_by_provider(\n        session,\n        user_id=user_id,\n        deployment_provider_account_id=provider_account.id,\n    )","sourceCodeStart":198,"sourceCodeEnd":234,"githubUrl":"https://github.com/langflow-ai/langflow/blob/976ec789d2886a86de109c044d089d68e96c9a35/src/backend/base/langflow/api/v1/deployments.py#L198-L234","documentation":"Raised as HTTP 409 by _raise_http_for_provider_account_value_error when creating/updating a deployment provider account would duplicate an existing record: the underlying ValueError message contains 'already exists' or 'conflicts with an existing record'. It is a deliberate translation of a service-layer uniqueness check into a RESTful conflict response with a stable, client-displayable message.","triggerScenarios":"POST /api/v1/deployments/providers (or PUT on a provider account) with a (provider_type, identifier) combination the same user has already registered — e.g. adding the same GitHub/OAuth/app-provider account twice.","commonSituations":"Double-submitting the 'add provider account' form; retrying a timed-out create that actually succeeded; re-connecting an OAuth integration that is already linked; UI not refreshing the account list.","solutions":["List existing provider accounts (GET /deployments/providers) — the account is likely already there; use it instead of re-creating.","If you want a fresh link, delete the existing account first, then re-create.","Guard the create button/form against double submission (disable on in-flight).","On 409, treat the existing record as the outcome rather than surfacing an error to the user."],"exampleFix":"# before\nres = await client.post(\"/api/v1/deployments/providers\", json=payload)\nres.raise_for_status()  # 409 explodes\n\n# after\nif res.status_code == 409:\n    accounts = (await client.get(\"/api/v1/deployments/providers\")).json()\n    account = next(a for a in accounts if a[\"provider_type\"] == payload[\"provider_type\"])\nelse:\n    res.raise_for_status()","handlingStrategy":"try-catch","validationCode":"# Check for an existing provider account before creating\naccounts = (await client.get(\"/api/v1/deployments/providers\")).json()\nif any(a[\"provider_type\"] == payload[\"provider_type\"] and a[\"identifier\"] == payload[\"identifier\"] for a in accounts):\n    return  # already tracked; reuse it","typeGuard":null,"tryCatchPattern":"try:\n    res = await client.post(\"/api/v1/deployments/providers\", json=payload)\nexcept httpx.HTTPStatusError as e:\n    if e.response.status_code == 409:\n        return await get_existing_provider_account(payload)  # idempotent outcome\n    raise","preventionTips":["Disable submit buttons while a create is in flight (double-submit is the top cause).","After a timed-out create, list accounts before retrying — it may have succeeded.","Model 'already linked' as a success path in UI flows."],"tags":["langflow","deployments","conflict","http-409"],"backgroundTag":null,"analyzedSha":"976ec789d2886a86de109c044d089d68e96c9a35","analyzedAt":"2026-08-14T18:23:12.227Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}