When a session can't find a connected account

A session only sees connected accounts that match its project and its user_id. An account can show as active in the dashboard and still be invisible to your session if it was created under a different user ID or auth config.

How the pieces fit

  • Project. Your API key picks the project. Auth configs, connected accounts, and sessions never cross projects.
  • Auth config. How a toolkit authenticates: Composio managed auth or your own credentials. See Managed vs custom auth.
  • Connected account. One user's credentials for one auth config. Each account stores the user_id it was created for.
  • Session. Created for one user_id. At execution time it picks an ACTIVE account that belongs to that user, unless you pin one with connectedAccounts.

Use your application's own stable user ID everywhere: when you create sessions and when you connect accounts. See userID best practices.

Find the user ID that owns the account

Look up the account by its ID (ca_..., shown in the dashboard) and read its user_id:

account = composio.connected_accounts.get("ca_123")

print(account.user_id, account.status, account.auth_config.id)

If the user_id isn't the one your session uses, the session won't find the account. This happens most often with accounts connected outside your app, such as from the dashboard or a developer tool. For example, composio dev init writes a playground test user (pg-test-<your member ID>) to COMPOSIO_TEST_USER_ID, which is not one of your application's users.

You can't change the user_id of an existing connected account. Reconnect it under the right user instead.

Check the rest of the match

If the user ID matches and the session still can't use the account, check these in order:

  1. Status. The account must be ACTIVE and not disabled. INITIATED means the user never finished the flow. EXPIRED means it needs a new grant. See connection status.
  2. Toolkit restrictions. If the session sets toolkits, the toolkit must be allowed. Toolkit restrictions apply before account lookup.
  3. Auth config. If the session passes authConfigs for the toolkit, the account must belong to that auth config.
  4. Tool Router flag. Auth configs that sessions create are enabled for Tool Router automatically. If you created the auth config yourself, in the dashboard or the API, set is_enabled_for_tool_router to true so sessions can match its accounts by user_id.
  5. Pinned account. If the session passes connectedAccounts for the toolkit, it uses exactly that account and never falls back to another one. Update the session or create a new one to change it.

Enable the Tool Router flag on an auth config you created:

composio.auth_configs.update(
    "ac_your_config",
    options={"type": "custom", "is_enabled_for_tool_router": True},
)

Reconnect under the right user

Create a session for your application's user ID, then authorize the toolkit from that session. The new connected account belongs to that user, so this session and later sessions for the same user can use it. Pass authConfigs if the toolkit uses your own auth config.

session = composio.create(
    user_id="user_123",
    auth_configs={"gmail": "ac_your_config"},
)

connection_request = session.authorize("gmail")
print(connection_request.redirect_url)  # Send the user here

connection_request.wait_for_connection(60)

# Confirm the session sees the account
for toolkit in session.toolkits(is_connected=True).items:
    account = toolkit.connection and toolkit.connection.connected_account
    print(toolkit.slug, account.id if account else None)

Only send the Connect Link to the person who owns that user ID. Whoever completes the flow decides which external account gets attached to user_123.

Once the new account is active, delete or disable the old one under the wrong user ID if nobody needs it. See Connected accounts.