can1357/oh-my-pi · error

Security OAuth credential ${requestedCredentialId} is not av

Error message

Security OAuth credential ${requestedCredentialId} is not available for ${provider}

What it means

When a specific credentialId is requested, selectSecurityAccount must find an account with exactly that credential id among the provider's stored OAuth accounts. If the id exists in the request but matches no stored account, this error is thrown (distinct from the zero-accounts and multiple-accounts cases).

Source

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

		throw new Error("Security scan authentication identity mismatch");
	}
}

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 }>> {

View on GitHub (pinned to 9690622007)

Solutions

  1. List available accounts (authStorage.listOAuthAccounts(provider)) and use a valid credentialId
  2. Omit requestedCredentialId to let the active (or sole) account be selected automatically
  3. Re-authenticate if the credential was removed or rotated

Example fix

// before
selectSecurityAccount(authStorage, "github", "cred-123", sessionId); // no longer exists
// after
selectSecurityAccount(authStorage, "github", undefined, sessionId); // use active account
Defensive patterns

Strategy: validation

Validate before calling

const accounts = authStorage.listOAuthAccounts(provider, sessionId);
if (requestedCredentialId && !accounts.some(a => a.credentialId === requestedCredentialId)) {
  throw new Error(`credentialId ${requestedCredentialId} not found for ${provider}`);
}

Type guard

null

Try / catch

try {
  const account = selectSecurityAccount(authStorage, provider, requestedCredentialId, sessionId);
} catch (err) {
  if (err instanceof Error && err.message.includes("is not available for")) {
    const account = selectSecurityAccount(authStorage, provider, undefined, sessionId); // fall back to active
  } else throw err;
}

Prevention

When it happens

Trigger: Passing a requestedCredentialId that is not present in authStorage.listOAuthAccounts(provider, sessionId) — e.g. a credential that was removed, rotated, or belongs to a different provider/session scope.

Common situations: Referencing a credential id after re-authentication rotated it; hardcoding a credentialId from another machine or config; typo'd id; session-scoped lookup hiding the credential.

Related errors


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