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
- Pass options.modelRegistry alone and let the session use modelRegistry.authStorage
- Pass options.authStorage alone without a modelRegistry so one is created internally sharing the authStorage
- 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
- Construct AuthStorage first and pass the same instance into both ModelRegistry and options.authStorage
- Prefer passing only modelRegistry and letting the session use its authStorage
- Write a small helper that builds the session config so instances can never diverge
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
- gitlab-duo-agent
- No API key for ${resolved.model.provider}/${resolved.model.i
- No available model to generate agent specification.
- Security scans require a stored OAuth account for ${provider
- Security OAuth credential ${requestedCredentialId} is not av
AI-assisted analysis of can1357/oh-my-pi@9690622007 (2026-08-31).
Data as JSON: /api/errors/802372eb6cd1af81.
Report an issue: GitHub.