ruvnet/ruflo · error
module loaded but is missing expected OAuth exports
Error message
module loaded but is missing expected OAuth exports
What it means
loadSecurityOAuth() dynamically imports @claude-flow/security and probes for authorizeUrl/createKeychainAdapter. If the module resolves but those exports are absent, the install is broken or the resolved version's API doesn't match what the CLI expects (version skew, partial install, a same-named package shadowing it). Note the probe throw happens inside the try, so callers actually receive SecurityPackageMissingError whose underlying message is this string.
Solutions
- Align versions: install the @claude-flow/security version the CLI declares (check @claude-flow/cli's package.json optionalDependencies), e.g. npm install @claude-flow/security@<matching-version>
- Clean reinstall: rm -rf node_modules package-lock.json && npm install
- Verify what actually resolves: node -e "import('@claude-flow/security').then(m => console.log(Object.keys(m)))" — confirm authorizeUrl and createKeychainAdapter are present
- Check for duplicate installs / path shadowing: npm ls @claude-flow/security
Example fix
# before
npm install @claude-flow/cli @claude-flow/security@0.9.0 # mismatched versions
# after
npm install @claude-flow/cli && npm install @claude-flow/security@$(node -p "require('@claude-flow/cli/package.json').optionalDependencies['@claude-flow/security']") Defensive patterns
Strategy: try-catch
Validate before calling
// Pre-flight: confirm the installed security package exposes the OAuth surface
const mod = await import('@claude-flow/security');
if (typeof mod.authorizeUrl !== 'function' || typeof mod.createKeychainAdapter !== 'function') {
throw new Error('incompatible @claude-flow/security version — align it with @claude-flow/cli');
} Type guard
import { SecurityPackageMissingError } from '@claude-flow/cli/dist/auth/security-bridge.js';
function isSecurityPackageProblem(e: unknown): e is SecurityPackageMissingError {
return e instanceof Error && e.name === 'SecurityPackageMissingError';
} Try / catch
try {
await runAuthCommand();
} catch (e) {
if (isSecurityPackageProblem(e) && e.message.includes('missing expected OAuth exports')) {
console.error('Version mismatch: install the @claude-flow/security version declared by @claude-flow/cli');
process.exit(5);
}
throw e;
} Prevention
- Pin @claude-flow/security to the exact version @claude-flow/cli declares in optionalDependencies
- Run npm ls @claude-flow/security in CI to detect duplicate/mismatched installs
- When mocking the package in tests, stub authorizeUrl and createKeychainAdapter too
When it happens
Trigger: An @claude-flow/security version older/newer than the CLI's expected OAuth surface is installed (mixed-version monorepo or pinned old version); node_modules partially written by an interrupted install; a bundler/test mock resolving in place of the real package; a rogue package with the same name earlier on the resolution path.
Common situations: npm workspaces/monorepos where a different package pinned an incompatible @claude-flow/security version; `npm install --force` or interrupted installs leaving stub modules; test suites that mock '@claude-flow/security' incompletely (missing authorizeUrl/createKeychainAdapter).
Related errors
- ruflo auth needs the '@claude-flow/security' package, which…
- AIDefence installed but failed to load
- Failed to import OpenAI
- abBenchmark requires a content-aware executor. The provided…
- AIDefence failed to load
AI-assisted analysis of ruvnet/ruflo@fa13ee4ad6 (2026-08-18).
Data as JSON: /api/errors/c094f2101b2476fd.
Report an issue: GitHub.
Appendix: source
Thrown at v3/@claude-flow/cli/src/auth/security-bridge.ts:70
"ruflo auth needs the '@claude-flow/security' package, which isn't installed " +
"(it's an optional dependency — install/reinstall failed or was skipped for this " +
`platform). Try: npm install @claude-flow/security. Underlying error: ${
cause instanceof Error ? cause.message : String(cause)
}`,
);
this.name = 'SecurityPackageMissingError';
}
}
let cached: SecurityOAuthModule | null = null;
/** Loads `@claude-flow/security`'s OAuth surface, throwing a clear error if it's absent. */
export async function loadSecurityOAuth(): Promise<SecurityOAuthModule> {
if (cached) return cached;
try {
const mod = (await import('@claude-flow/security')) as unknown as SecurityOAuthModule;
if (!mod.authorizeUrl || !mod.createKeychainAdapter) {
throw new Error('module loaded but is missing expected OAuth exports');
}
cached = mod;
return mod;
} catch (e) {
throw new SecurityPackageMissingError(e);
}
}
View on GitHub (pinned to fa13ee4ad6)