{"record":{"id":"2f73db49e7bf1187","repo":"bytedance/deer-flow","slug":"too-many-pending-channel-connection-codes-wait-fo","errorCode":null,"errorMessage":"Too many pending channel connection codes. Wait for existing codes to expire or use one of them.","messagePattern":"Too many pending channel connection codes\\. Wait for existing codes to expire or use one of them\\.","errorType":"http","errorClass":"HTTPException","httpStatus":429,"severity":"warning","filePath":"backend/app/gateway/routers/channel_connections.py","lineNumber":346,"sourceCode":"    repo: ChannelConnectionRepository,\n    *,\n    owner_user_id: str,\n    provider: str,\n) -> str:\n    now = datetime.now(UTC)\n    state = _new_binding_code()\n    # Atomic delete-expired + count + insert so concurrent connect POSTs from one\n    # owner cannot each see count < cap and all insert past the cap.\n    inserted = await repo.create_oauth_state_within_cap(\n        owner_user_id=owner_user_id,\n        provider=provider,\n        state=state,\n        expires_at=now + timedelta(seconds=_STATE_TTL_SECONDS),\n        max_pending=_MAX_PENDING_CONNECT_CODES_PER_PROVIDER,\n        now=now,\n    )\n    if not inserted:\n        raise HTTPException(\n            status_code=429,\n            detail=\"Too many pending channel connection codes. Wait for existing codes to expire or use one of them.\",\n        )\n    return state\n\n\ndef _connect_instruction(provider: str, code: str) -> str:\n    if provider == \"telegram\":\n        return f\"Send /start {code} to the DeerFlow Telegram bot.\"\n    meta = _PROVIDER_META.get(provider)\n    if meta is None:\n        raise HTTPException(status_code=404, detail=\"Unknown channel provider\")\n    return f\"Send /connect {code} to the DeerFlow {meta['display_name']} bot.\"\n\n\ndef _connect_url(config: ChannelConnectionsConfig, provider: str, code: str) -> str | None:\n    if provider == \"telegram\":\n        provider_config = _provider_config(config, provider)","sourceCodeStart":328,"sourceCodeEnd":364,"githubUrl":"https://github.com/bytedance/deer-flow/blob/1dd6ba1acb03700589994b0366c5d1c7d05e2eff/backend/app/gateway/routers/channel_connections.py#L328-L364","documentation":"429 from the connect-code creation helper (channel_connections.py:346): repo.create_oauth_state_within_cap() returned false, meaning the owner already has _MAX_PENDING_CONNECT_CODES_PER_PROVIDER (5) unexpired binding codes for this provider. The insert is an atomic delete-expired + count + insert so concurrent POSTs from one owner cannot each see count < cap and all insert past it. Codes expire after _STATE_TTL_SECONDS (600s = 10 minutes); this is per-user-per-provider throttling of pending connect codes.","triggerScenarios":"POSTing /api/channels/connections/{provider}/connect (or equivalent code-issuing endpoint) six or more times within 10 minutes without completing or expiring the earlier codes — e.g. a UI retry loop, repeated button clicks, or an automation that requests a fresh code per attempt.","commonSituations":"User repeatedly clicking 'generate connect code'; frontend re-requesting a code on every render/poll; scripts minting codes without consuming them.","solutions":["Reuse one of the already-issued pending codes (the error text says exactly this) — they remain valid until they expire","Wait for the oldest codes to expire (up to 10 minutes, _STATE_TTL_SECONDS=600) before requesting a new one","Fix the client: request a code once and display/reuse it; do not loop on code creation — respect 429 with the standard Retry-After backoff posture"],"exampleFix":"// before — frontend mints a code on every render\nuseEffect(() => { postConnect(provider); }, [provider]);\n\n// after — mint once, reuse while pending\nconst [code, setCode] = useState(null);\nuseEffect(() => {\n  if (!code) postConnect(provider).then(r => setCode(r.code)).catch(e => {\n    if (e.status === 429) showPendingCodes(e); // surface existing codes\n  });\n}, []);","handlingStrategy":"retry","validationCode":"# Client: track pending codes per provider before requesting more\nconst pending = await fetchPendingCodes(provider);\nif (pending.length >= 5) return pending[0]; // reuse instead of minting","typeGuard":null,"tryCatchPattern":"try { code = await createConnectCode(provider) }\ncatch (e) {\n  if (e.status === 429) { const codes = await fetchPendingCodes(provider); return codes[0]; } // reuse existing\n  throw e;\n}","preventionTips":["Mint a connect code once per user action and reuse it until used or expired (10-minute TTL)","Rate-limit the 'generate code' button in the UI and show pending codes on 429","Treat 429 as 'wait or reuse', not an error to hammer — the cap is per provider per owner (5 pending)"],"tags":["channels","rate-limit","http-429","throttling"],"backgroundTag":null,"analyzedSha":"1dd6ba1acb03700589994b0366c5d1c7d05e2eff","analyzedAt":"2026-08-14T21:20:34.804Z","schemaVersion":2},"datasetVersion":"2026-08-15T22:17:37.221Z"}