{"record":{"id":"802372eb6cd1af81","repo":"can1357/oh-my-pi","slug":"options-authstorage-and-options-modelregistry-auth","errorCode":null,"errorMessage":"options.authStorage and options.modelRegistry.authStorage must be the same instance when both are provided","messagePattern":"options\\.authStorage and options\\.modelRegistry\\.authStorage must be the same instance when both are provided","errorType":"validation","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/sdk.ts","lineNumber":1325,"sourceCode":"\t// Pin authStorage to modelRegistry.authStorage: ModelRegistry.getApiKey() routes refresh\n\t// failures through that instance, so any divergent storage handed to the bridge / mcpManager\n\t// / session would silently miss credential_disabled events.\n\tconst modelRegistry =\n\t\toptions.modelRegistry ??\n\t\tnew ModelRegistry(\n\t\t\toptions.authStorage ?? (await logger.time(\"discoverModels\", discoverAuthStorage, agentDir)),\n\t\t\tpath.join(agentDir, \"models.yml\"),\n\t\t\t{\n\t\t\t\tsettings,\n\t\t\t\tcacheDbPath: getModelDbPath(agentDir),\n\t\t\t},\n\t\t);\n\t// Track whether we internally created the authStorage so we can close it\n\t// if construction fails before the session takes ownership.\n\tconst ownsAuthStorage = !options.authStorage && !options.modelRegistry;\n\tconst authStorage = modelRegistry.authStorage;\n\tif (options.authStorage && options.authStorage !== authStorage) {\n\t\tthrow new Error(\n\t\t\t\"options.authStorage and options.modelRegistry.authStorage must be the same instance when both are provided\",\n\t\t);\n\t}\n\t// Subscribe before any getApiKey() call so startup model probes can't fire a\n\t// credential_disabled event past us. An embedder's constructor handler makes the\n\t// listener set non-empty from construction, which defeats AuthStorage's no-listener\n\t// buffer — so we can't rely on it to catch startup events for the extension runner.\n\tconst startupCredentialDisabledEvents: CredentialDisabledEvent[] = [];\n\tlet credentialDisabledTarget: ExtensionRunner | undefined;\n\tconst unsubscribeCredentialDisabled: (() => void) | undefined = authStorage.onCredentialDisabled(event => {\n\t\tif (credentialDisabledTarget) {\n\t\t\t// Discard return: any handler error is routed through runner.onError listeners.\n\t\t\tvoid credentialDisabledTarget.emitCredentialDisabled(event);\n\t\t} else {\n\t\t\tstartupCredentialDisabledEvents.push(event);\n\t\t}\n\t});\n\tawait modelRegistry.hydrateCredentialScopedModelCaches();","sourceCodeStart":1307,"sourceCodeEnd":1343,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/sdk.ts#L1307-L1343","documentation":"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.","triggerScenarios":"Calling createAgentSession/createAgentSessionScoped with options.authStorage set AND options.modelRegistry set, where modelRegistry.authStorage !== options.authStorage (two separately constructed AuthStorage instances).","commonSituations":"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.","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"],"exampleFix":"// before\nconst registry = new ModelRegistry(new AuthStorage(dir));\nawait createAgentSession({ authStorage: new AuthStorage(dir), modelRegistry: registry });\n// after\nconst auth = new AuthStorage(dir);\nconst registry = new ModelRegistry(auth);\nawait createAgentSession({ modelRegistry: registry });","handlingStrategy":"validation","validationCode":"if (options.authStorage && options.modelRegistry && options.authStorage !== options.modelRegistry.authStorage) {\n  throw new Error(\"authStorage instances differ; pass the same instance or omit options.authStorage\");\n}","typeGuard":"null","tryCatchPattern":"try {\n  session = await createAgentSession(options);\n} catch (err) {\n  if (err instanceof Error && err.message.includes(\"must be the same instance\")) {\n    session = await createAgentSession({ ...options, authStorage: undefined });\n  } else throw err;\n}","preventionTips":["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"],"tags":["configuration","sdk","auth"],"backgroundTag":"mismatched-dependency-injection","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}