{"record":{"id":"c094f2101b2476fd","repo":"ruvnet/ruflo","slug":"module-loaded-but-is-missing-expected-oauth-export","errorCode":null,"errorMessage":"module loaded but is missing expected OAuth exports","messagePattern":"module loaded but is missing expected OAuth exports","errorType":"exception","errorClass":"Error","httpStatus":null,"severity":"error","filePath":"v3/@claude-flow/cli/src/auth/security-bridge.ts","lineNumber":70,"sourceCode":"      \"ruflo auth needs the '@claude-flow/security' package, which isn't installed \" +\n        \"(it's an optional dependency — install/reinstall failed or was skipped for this \" +\n        `platform). Try: npm install @claude-flow/security. Underlying error: ${\n          cause instanceof Error ? cause.message : String(cause)\n        }`,\n    );\n    this.name = 'SecurityPackageMissingError';\n  }\n}\n\nlet cached: SecurityOAuthModule | null = null;\n\n/** Loads `@claude-flow/security`'s OAuth surface, throwing a clear error if it's absent. */\nexport async function loadSecurityOAuth(): Promise<SecurityOAuthModule> {\n  if (cached) return cached;\n  try {\n    const mod = (await import('@claude-flow/security')) as unknown as SecurityOAuthModule;\n    if (!mod.authorizeUrl || !mod.createKeychainAdapter) {\n      throw new Error('module loaded but is missing expected OAuth exports');\n    }\n    cached = mod;\n    return mod;\n  } catch (e) {\n    throw new SecurityPackageMissingError(e);\n  }\n}\n","sourceCodeStart":52,"sourceCodeEnd":78,"githubUrl":"https://github.com/ruvnet/ruflo/blob/6b01dc5a687b26b3e218f796de45ec51f8fa9e8c/v3/@claude-flow/cli/src/auth/security-bridge.ts#L52-L78","documentation":"Internal guard inside loadSecurityOAuth(). Fires when the dynamic import('@claude-flow/security') resolved successfully (the package IS installed and resolvable) but the resulting module object lacks the expected authorizeUrl or createKeychainAdapter exports. This signals a version mismatch or package shadowing. The bare Error thrown here is immediately caught by the surrounding try/catch and re-wrapped as SecurityPackageMissingError (error 162), so end users normally see 162's message — 161 only surfaces if you inspect the cause chain.","triggerScenarios":"loadSecurityOAuth() is called (by any ruflo auth path, browserLogin, manualLogin, tokenStdinLogin, or refreshAccessToken) and import('@claude-flow/security') resolves to a module whose authorizeUrl or createKeychainAdapter property is falsy.","commonSituations":"A stale lockfile pins an old @claude-flow/security that predates the createKeychainAdapter export; a monorepo hoisting resolves a workspace-local stub or fork; an `overrides`/`resolutions` field in package.json redirects the package name to an incompatible implementation; a partial publish left the package with missing entry points.","solutions":["Verify the resolved version: run `npm ls @claude-flow/security` and confirm it matches what the CLI expects","Upgrade to the latest: `npm install @claude-flow/security@latest`","Clear the lockfile and node_modules, then reinstall to resolve hoisting shadowing in a monorepo","Check package.json for `overrides` (npm) or `resolutions` (yarn) that redirect @claude-flow/security to a fork"],"exampleFix":"// before: stale lockfile resolves an old security package without createKeychainAdapter\n// package-lock.json pins @claude-flow/security@0.1.0\n\n// after:\n// npm install @claude-flow/security@latest\n// now loadSecurityOAuth() finds both authorizeUrl and createKeychainAdapter exports","handlingStrategy":"try-catch","validationCode":null,"typeGuard":"import type { SecurityOAuthModule } from '@claude-flow/cli/auth/security-bridge';\n\nasync function isSecurityModuleUsable(): Promise<boolean> {\n  try {\n    const mod = await import('@claude-flow/security');\n    return typeof (mod as any).authorizeUrl === 'function'\n        && typeof (mod as any).createKeychainAdapter === 'function';\n  } catch {\n    return false;\n  }\n}","tryCatchPattern":"import { loadSecurityOAuth, SecurityPackageMissingError } from '@claude-flow/cli/auth/security-bridge';\n\ntry {\n  const sec = await loadSecurityOAuth();\n  // use sec.authorizeUrl(...)\n} catch (e) {\n  if (e instanceof SecurityPackageMissingError) {\n    console.error(e.message); // includes the underlying cause + install command\n    process.exit(1);\n  }\n  throw e;\n}","preventionTips":["Pin @claude-flow/security to a version compatible with the CLI version you are using","In CI, run `npm ls @claude-flow/security` as a smoke test before invoking ruflo auth","Avoid `overrides`/`resolutions` that redirect the security package to a fork unless the fork exports the same surface"],"tags":["auth","dependencies","version-mismatch","security-package"],"backgroundTag":null,"analyzedSha":"6b01dc5a687b26b3e218f796de45ec51f8fa9e8c","analyzedAt":"2026-08-12T13:20:50.148Z","schemaVersion":2},"datasetVersion":"2026-08-12T18:17:37.767Z"}