{"record":{"id":"feb14ea20b2b877f","repo":"can1357/oh-my-pi","slug":"options-authstorage-and-options-modelregistry-auth-feb14e","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":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"packages/coding-agent/src/task/executor.ts","lineNumber":2975,"sourceCode":"\t\t// Launch-latency phase marks (performance.now()); read by the debug log\n\t\t// emitted before this closure returns. Left undefined when setup throws\n\t\t// before reaching the phase, which itself localizes the cost.\n\t\tconst perfStart = performance.now();\n\t\tlet resolvedAt: number | undefined;\n\t\tlet sessionOpenedAt: number | undefined;\n\t\tlet sessionCreatedAt: number | undefined;\n\t\tlet readyAt: number | undefined;\n\n\t\ttry {\n\t\t\tcheckAbort();\n\t\t\t// Pin authStorage to modelRegistry.authStorage — mirrors the createAgentSession invariant.\n\t\t\tconst registryFromParent = options.modelRegistry !== undefined;\n\t\t\tconst modelRegistry =\n\t\t\t\toptions.modelRegistry ??\n\t\t\t\tnew ModelRegistry(options.authStorage ?? (await awaitAbortable(discoverAuthStorage())));\n\t\t\tconst authStorage = modelRegistry.authStorage;\n\t\t\tif (options.authStorage && options.authStorage !== authStorage) {\n\t\t\t\tthrow new Error(\n\t\t\t\t\t\"options.authStorage and options.modelRegistry.authStorage must be the same instance when both are provided\",\n\t\t\t\t);\n\t\t\t}\n\t\t\tcheckAbort();\n\t\t\tif (!registryFromParent) {\n\t\t\t\tmodelRegistry.refreshInBackground();\n\t\t\t} else {\n\t\t\t\tlogger.debug(\"runSubagent: reusing parent modelRegistry; skipping refresh\");\n\t\t\t}\n\t\t\tcheckAbort();\n\n\t\t\tconst configuredModelPatterns = resolveConfiguredModelPatterns(modelPatterns, settings);\n\t\t\tconst inheritedRetryFallbackChain =\n\t\t\t\tconfiguredModelPatterns.length === 1\n\t\t\t\t\t? resolveSubagentInheritedRetryFallbackChain(\n\t\t\t\t\t\t\tsubagentSettings,\n\t\t\t\t\t\t\tmodelRegistry,\n\t\t\t\t\t\t\tmodelRole ?? resolveExplicitModelRole(modelPatterns, subagentSettings),","sourceCodeStart":2957,"sourceCodeEnd":2993,"githubUrl":"https://github.com/can1357/oh-my-pi/blob/969062200754ea02cfac922e5ebb8c608c079e15/packages/coding-agent/src/task/executor.ts#L2957-L2993","documentation":"When constructing the executor, if the caller supplies both options.authStorage and options.modelRegistry, the code derives authStorage from modelRegistry and verifies identity. Passing two different AuthStorage instances is ambiguous (two sources of truth for credentials), so it throws.","triggerScenarios":"Creating the task executor with options = { authStorage: instanceA, modelRegistry: registryB } where registryB.authStorage !== instanceA — the mismatch check `options.authStorage && options.authStorage !== authStorage` fires.","commonSituations":"Embedding the SDK and constructing a ModelRegistry with its own discovered auth storage while separately passing a custom AuthStorage, wiring two different profile directories, or caching instances across config reloads so one became stale.","solutions":["Pass only options.modelRegistry built with your AuthStorage: new ModelRegistry(authStorage), and omit options.authStorage.","Or pass only options.authStorage and let the executor create the ModelRegistry.","If both must be passed, ensure they are the same instance: registry.authStorage === authStorage.","Audit instance caching — avoid constructing a fresh AuthStorage after the registry was already built."],"exampleFix":"// before\nnew Executor({ authStorage: myAuth, modelRegistry: new ModelRegistry(discoverAuthStorage()) })\n// after\nnew Executor({ modelRegistry: new ModelRegistry(myAuth) })","handlingStrategy":"validation","validationCode":"function assertConsistentAuth(opts: { authStorage?: AuthStorage; modelRegistry?: ModelRegistry }): void {\n  if (opts.authStorage && opts.modelRegistry && opts.modelRegistry.authStorage !== opts.authStorage) {\n    throw new Error(\"authStorage must be modelRegistry.authStorage when both are passed\");\n  }\n}","typeGuard":null,"tryCatchPattern":"try {\n  const executor = new Executor(options);\n} catch (err) {\n  if (String(err.message).includes(\"must be the same instance\")) {\n    // rebuild with a single source of truth\n    return new Executor({ modelRegistry: new ModelRegistry(options.authStorage) });\n  }\n  throw err;\n}","preventionTips":["Adopt one DI pattern: always construct ModelRegistry from your AuthStorage and pass only the registry.","Memoize the AuthStorage instance per profile directory so all consumers share it.","Never construct a second AuthStorage after the registry exists."],"tags":["api-misuse","configuration","invariant","auth"],"backgroundTag":"inconsistent-dependency-injection","analyzedSha":"969062200754ea02cfac922e5ebb8c608c079e15","analyzedAt":"2026-08-31T10:29:35.737Z","schemaVersion":2},"datasetVersion":"2026-08-31T14:17:45.589Z"}