can1357/oh-my-pi · error · Error

options.authStorage and options.modelRegistry.authStorage mu

Error message

options.authStorage and options.modelRegistry.authStorage must be the same instance when both are provided

What it means

createAgentSessionScoped accepts both an options.authStorage and a modelRegistry that carries its own authStorage. Because the session takes ownership of exactly one credential store, providing two different AuthStorage instances would make API-key resolution ambiguous, so the constructor fails fast.

Source

Thrown at packages/coding-agent/src/sdk.ts:1325

	// Pin authStorage to modelRegistry.authStorage: ModelRegistry.getApiKey() routes refresh
	// failures through that instance, so any divergent storage handed to the bridge / mcpManager
	// / session would silently miss credential_disabled events.
	const modelRegistry =
		options.modelRegistry ??
		new ModelRegistry(
			options.authStorage ?? (await logger.time("discoverModels", discoverAuthStorage, agentDir)),
			path.join(agentDir, "models.yml"),
			{
				settings,
				cacheDbPath: getModelDbPath(agentDir),
			},
		);
	// Track whether we internally created the authStorage so we can close it
	// if construction fails before the session takes ownership.
	const ownsAuthStorage = !options.authStorage && !options.modelRegistry;
	const authStorage = modelRegistry.authStorage;
	if (options.authStorage && options.authStorage !== authStorage) {
		throw new Error(
			"options.authStorage and options.modelRegistry.authStorage must be the same instance when both are provided",
		);
	}
	// Subscribe before any getApiKey() call so startup model probes can't fire a
	// credential_disabled event past us. An embedder's constructor handler makes the
	// listener set non-empty from construction, which defeats AuthStorage's no-listener
	// buffer — so we can't rely on it to catch startup events for the extension runner.
	const startupCredentialDisabledEvents: CredentialDisabledEvent[] = [];
	let credentialDisabledTarget: ExtensionRunner | undefined;
	const unsubscribeCredentialDisabled: (() => void) | undefined = authStorage.onCredentialDisabled(event => {
		if (credentialDisabledTarget) {
			// Discard return: any handler error is routed through runner.onError listeners.
			void credentialDisabledTarget.emitCredentialDisabled(event);
		} else {
			startupCredentialDisabledEvents.push(event);
		}
	});
	await modelRegistry.hydrateCredentialScopedModelCaches();

View on GitHub (pinned to 9690622007)

Solutions

  1. Pass options.modelRegistry alone and let the session use modelRegistry.authStorage
  2. Pass options.authStorage alone without a modelRegistry so one is created internally sharing the authStorage
  3. Ensure the AuthStorage given as options.authStorage is the exact same instance the ModelRegistry was constructed with

Example fix

// before
const registry = new ModelRegistry(new AuthStorage(dir));
await createAgentSession({ authStorage: new AuthStorage(dir), modelRegistry: registry });
// after
const auth = new AuthStorage(dir);
const registry = new ModelRegistry(auth);
await createAgentSession({ modelRegistry: registry });
Defensive patterns

Strategy: validation

Validate before calling

if (options.authStorage && options.modelRegistry && options.authStorage !== options.modelRegistry.authStorage) {
  throw new Error("authStorage instances differ; pass the same instance or omit options.authStorage");
}

Type guard

null

Try / catch

try {
  session = await createAgentSession(options);
} catch (err) {
  if (err instanceof Error && err.message.includes("must be the same instance")) {
    session = await createAgentSession({ ...options, authStorage: undefined });
  } else throw err;
}

Prevention

When it happens

Trigger: Calling createAgentSession/createAgentSessionScoped with options.authStorage set AND options.modelRegistry set, where modelRegistry.authStorage !== options.authStorage (two separately constructed AuthStorage instances).

Common situations: SDK embedders constructing their own ModelRegistry and AuthStorage independently, then passing a pre-existing authStorage alongside it; caching a ModelRegistry across sessions while creating a fresh AuthStorage per session.

Related errors


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