can1357/oh-my-pi · error

Multiple OAuth accounts are available for ${provider}; suppl

Error message

Multiple OAuth accounts are available for ${provider}; supply credentialId to pin one exact account

What it means

selectSecurityAccount() refuses to guess which stored OAuth account to use when a provider has more than one credential and none is marked active and no credentialId was supplied. The library requires an exact, unambiguous account reference before running security scans. This prevents scans from authenticating as the wrong identity.

Source

Thrown at packages/coding-agent/src/security/auth.ts:49

}

export function selectSecurityAccount(
	authStorage: AuthStorage,
	provider: string,
	requestedCredentialId?: number,
	sessionId?: string,
): SecurityAccountRef {
	const accounts = authStorage.listOAuthAccounts(provider, sessionId);
	const selected =
		requestedCredentialId !== undefined
			? accounts.find(account => account.credentialId === requestedCredentialId)
			: (accounts.find(account => account.active) ?? (accounts.length === 1 ? accounts[0] : undefined));
	if (!selected) {
		if (accounts.length === 0) throw new Error(`Security scans require a stored OAuth account for ${provider}`);
		if (requestedCredentialId !== undefined) {
			throw new Error(`Security OAuth credential ${requestedCredentialId} is not available for ${provider}`);
		}
		throw new Error(
			`Multiple OAuth accounts are available for ${provider}; supply credentialId to pin one exact account`,
		);
	}
	const account: SecurityAccountRef = { provider, credentialId: selected.credentialId };
	if (selected.accountId !== undefined) account.accountId = selected.accountId;
	if (selected.email !== undefined) account.email = selected.email;
	if (selected.orgId !== undefined) account.organizationId = selected.orgId;
	if (selected.orgName !== undefined) account.organizationName = selected.orgName;
	return account;
}

export async function resolveExactSecurityOAuthAccess(
	authStorage: AuthStorage,
	account: SecurityAccountRef,
	options: { forceRefresh: boolean; signal?: AbortSignal },
): Promise<Extract<OAuthAccessResolution, { ok: true }>> {
	const resolution = await authStorage.getOAuthAccessByCredentialId(account.provider, account.credentialId, options);
	if (!resolution) throw new Error("The pinned security OAuth credential is unavailable");

View on GitHub (pinned to 9690622007)

Solutions

  1. Pass an explicit credentialId option to pin the account you want
  2. Mark one stored account as active in AuthStorage
  3. Remove stale/duplicate OAuth credentials for the provider so only one remains
  4. List stored credentials (auth storage contents) to find the correct credentialId

Example fix

// before
const account = selectSecurityAccount(authStorage, provider, undefined);
// after
const account = selectSecurityAccount(authStorage, provider, "cred_abc123");
Defensive patterns

Strategy: validation

Validate before calling

const accounts = authStorage.listOAuthAccounts(provider);
if (accounts.length > 1 && !accounts.some(a => a.active) && !credentialId) {
  throw new Error(`Pick one of ${accounts.length} ${provider} credentials: ${accounts.map(a => a.credentialId).join(", ")}`);
}

Try / catch

try {
  const account = selectSecurityAccount(authStorage, provider, credentialId);
} catch (err) {
  if (err.message.includes("Multiple OAuth accounts")) {
    // prompt user to choose / pass credentialId
  } else throw err;
}

Prevention

When it happens

Trigger: Calling security-scan flows (via selectSecurityAccount) for a provider that has 2+ stored OAuth credentials in AuthStorage, with no active flag set and no requestedCredentialId. Passing a credentialId only avoids this if it matches; otherwise the 'not available' variant throws instead.

Common situations: Developers who re-logged into the same provider (e.g. ChatGPT/openai-codex) twice, creating duplicate credentials; accounts synced from multiple machines; a previously-active credential lost its active flag after re-auth.

Related errors


AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31). Data as JSON: /api/errors/cb9202f703e7deec. Report an issue: GitHub.