can1357/oh-my-pi · error

The pinned security OAuth credential is unavailable

Error message

The pinned security OAuth credential is unavailable

What it means

resolveExactSecurityOAuthAccess() looks up the pinned credential row via authStorage.getOAuthAccessByCredentialId; when the lookup returns nothing (credential record no longer exists / not visible), it throws this error. It means the SecurityAccountRef points at a credential that is not present at resolution time.

Source

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

		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");
	assertSecurityIdentityMatches(account, resolution);
	if (!resolution.ok) throw new Error("The pinned security OAuth credential could not be resolved");
	return resolution;
}

/**
 * Build a request credential resolver pinned to one durable OAuth row.
 *
 * Initial resolution and refresh both target the same row. The auth driver's
 * final sibling-rotation step returns `undefined`, so an unavailable account
 * fails the scan rather than crossing an account/workspace boundary.
 */
export function createExactSecurityOAuthResolver(
	options: ExactSecurityOAuthOptions,
): NonNullable<AgentOptions["getApiKey"]> {
	const { account, authStorage } = options;
	return model => {
		if (model.provider !== account.provider) {

View on GitHub (pinned to 9690622007)

Solutions

  1. Re-list stored OAuth credentials and re-pin a valid credentialId
  2. Re-authenticate with the provider to recreate the credential
  3. Remove the stale pinned credentialId and let account selection pick a current one

Example fix

// before
resolveExactSecurityOAuthAccess(authStorage, { provider: "openai-codex", credentialId: "old-id" }, opts)
// after
const accounts = listAccounts(authStorage, "openai-codex");
resolveExactSecurityOAuthAccess(authStorage, { provider: "openai-codex", credentialId: accounts[0].credentialId }, opts)
Defensive patterns

Strategy: validation

Validate before calling

const stored = authStorage.listOAuthAccounts(account.provider);
if (!stored.some(a => a.credentialId === account.credentialId)) {
  throw new Error(`Credential ${account.credentialId} no longer stored for ${account.provider}`);
}

Try / catch

try {
  const access = await resolveExactSecurityOAuthAccess(storage, account, opts);
} catch (err) {
  if (err.message === "The pinned security OAuth credential is unavailable") {
    await reauthenticate(account.provider); // re-pin fresh credential
  } else throw err;
}

Prevention

When it happens

Trigger: Calling resolveExactSecurityOAuthAccess (or any flow using createExactSecurityOAuthResolver) with a SecurityAccountRef whose credentialId no longer exists in AuthStorage for that provider — e.g. the credential was deleted, logged out, or rotated to a new id between pinning and use.

Common situations: Stale credentialId captured earlier in a long-lived config after the user re-authenticated; logout wiped the credential store; running on a machine whose AuthStorage never had that credential.

Related errors


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